8 Star 22 Fork 10

leo / supervisor

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
watcher_win.cc 10.11 KB
一键复制 编辑 原始数据 按行查看 历史
leo 提交于 2020-09-22 15:19 . 更新Win32查找可执行文件的算法
#if defined(_WIN32)
#include "watcher.h"
#include "logger.h"
#include <Psapi.h>
#include <io.h>
#include <vector>
static int ProcessorNumber = 1;
static LONGLONG FileTimeToInteger(const FILETIME & ft) {
LARGE_INTEGER ret;
ret.HighPart = ft.dwHighDateTime;
ret.LowPart = ft.dwLowDateTime;
return ret.QuadPart;
}
Watcher & Watcher::Instance() {
static std::unique_ptr<Watcher> iIns;
if (!iIns) {
iIns.reset(new Watcher);
SYSTEM_INFO si;
::GetSystemInfo(&si);
ProcessorNumber = (int)si.dwNumberOfProcessors;
}
return *iIns;
}
void Watcher::Breath() {
static time_t nLastBreath = 0;
time_t nTick = time(NULL);
if (nTick == nLastBreath) return;
nLastBreath = nTick;
Json::Value iMsg;
iMsg["action"] = "update";
iMsg["watchers"] = Json::Value(Json::arrayValue);
for (auto & kv : _mWatcher) {
if (kv.second.emStatus != EStatus::Running) continue;
Tail(kv.first, kv.second);
HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_VM_READ, false, kv.second.nPid);
if (hProc == INVALID_HANDLE_VALUE) {
CatchExit(kv.first, kv.second, EStatus::Fatal);
continue;
}
DWORD nExitCode = 0;
::GetExitCodeProcess(hProc, &nExitCode);
if (nExitCode != STILL_ACTIVE) {
CatchExit(kv.first, kv.second, nExitCode == 0 ? EStatus::Stopped : EStatus::Fatal);
continue;
}
PROCESS_MEMORY_COUNTERS pmc;
ZeroMemory(&pmc, sizeof(PROCESS_MEMORY_COUNTERS));
pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS);
Json::Value iState;
iState["name"] = kv.first;
iState["vsz"] = "-";
::GetProcessMemoryInfo(hProc, &pmc, sizeof(PROCESS_MEMORY_COUNTERS));
iState["res"] = (int)pmc.PagefileUsage / 1024;
FILETIME ftCreation, ftExit, ftKernel, ftUser, ftNow;
::GetSystemTimeAsFileTime(&ftNow);
LONGLONG nNow = FileTimeToInteger(ftNow);
LONGLONG nPeriod = nNow - kv.second.nLastCalcCPU;
kv.second.nLastCalcCPU = nNow;
if (::GetProcessTimes(hProc, &ftCreation, &ftExit, &ftKernel, &ftUser)) {
LONGLONG nKernel = FileTimeToInteger(ftKernel);
LONGLONG nUser = FileTimeToInteger(ftUser);
LONGLONG nTotal = nKernel + nUser;
LONGLONG nDiff = nTotal - kv.second.nLastCPUTotal;
kv.second.nLastCPUTotal = nTotal;
iState["cpu"] = std::to_string(nDiff * 1.f / nPeriod);
} else {
iState["cpu"] = "-";
}
::CloseHandle(hProc);
iMsg["watchers"].append(iState);
}
if (iMsg["watchers"].size() > 0) Notify("/ws", iMsg);
}
void Watcher::Start(const std::string & sName, const std::string & sPath, const std::string &sCmd, int nRetry) {
auto it = _mWatcher.find(sName);
Info * pInfo = nullptr;
if (it != _mWatcher.end() && it->second.emStatus == EStatus::Running) return;
if (it == _mWatcher.end()) {
Info iInfo;
iInfo.nRetry = nRetry;
iInfo.sCmd = sCmd;
iInfo.sPath = sPath;
_mWatcher[sName] = iInfo;
pInfo = &_mWatcher[sName];
} else if (it->second.emStatus == EStatus::Running) {
return;
} else {
pInfo = &it->second;
}
PROCESS_INFORMATION pi;
STARTUPINFOA si;
SECURITY_ATTRIBUTES sa;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFOA));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
HANDLE hReader, hWriter;
if (!::CreatePipe(&hReader, &hWriter, &sa, 0)) {
LOG_ERR("Failed to create pipe for start watcher [%s]. Error: %d", sName.c_str(), ::GetLastError());
return;
}
si.cb = sizeof(STARTUPINFOA);
si.hStdError = hWriter;
si.hStdOutput = hWriter;
si.dwFlags |= STARTF_USESTDHANDLES;
char pCmd[512] = {0};
char pWorkDir[512] = {0};
if (!sPath.empty()) {
if (sPath[0] == '.') {
::GetFullPathNameA(sPath.c_str(), 512, pWorkDir, NULL);
} else {
strncpy(pWorkDir, sPath.c_str(), 512);
}
}
if (sCmd[0] == '.') {
std::string sExec;
sExec.append(pWorkDir);
sExec.append("\\");
sExec.append(sCmd);
std::vector<std::string> vSupportedSuffix;
vSupportedSuffix.push_back(".exe");
vSupportedSuffix.push_back(".bat");
vSupportedSuffix.push_back(".cmd");
bool bHasExtension = false;
for (size_t i = sExec.size() - 1; i > 0; i--) {
if (sExec[i] == '/' || sExec[i] == '\\') break;
if (sExec[i] == '.') {
std::string sExt = sExec.substr(i);
if (std::find(vSupportedSuffix.begin(), vSupportedSuffix.end(), sExt) != vSupportedSuffix.end()) bHasExtension = true;
break;
}
}
if (!bHasExtension) {
bool bFixup = false;
for (auto const& sSuffix : vSupportedSuffix) {
std::string sTest = sExec + sSuffix;
if (_access(sTest.c_str(), 0) == 0) {
sExec.append(sSuffix);
bFixup = true;
break;
}
}
if (!bFixup) {
LOG_ERR("Start %s ... [Failed] Path : %s, Cmd: %s, Executable NOT FOUND", sName.c_str(), pWorkDir, sCmd.c_str());
return;
}
}
::GetFullPathNameA(sExec.c_str(), 512, pCmd, NULL);
} else {
strncpy(pCmd, sCmd.c_str(), 512);
}
bool bOk = ::CreateProcessA(
NULL,
pCmd,
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
pWorkDir,
&si,
&pi);
::CloseHandle(hWriter);
if (!bOk) {
::CloseHandle(hReader);
pInfo->emStatus = EStatus::Fatal;
LOG_ERR("Start %s ... [Failed] Path : %s, Cmd: %s", sName.c_str(), pWorkDir, pCmd);
return;
}
FILETIME ftNow;
::GetSystemTimeAsFileTime(&ftNow);
pInfo->hPipe = hReader;
pInfo->nPid = pi.dwProcessId;
pInfo->nLastCalcCPU = FileTimeToInteger(ftNow);
pInfo->nLastCPUTotal = 0;
pInfo->emStatus = EStatus::Running;
pInfo->nStart = time(NULL);
LOG_INFO("Start %s ... [OK]. PID : %d", sName.c_str(), pInfo->nPid);
Json::Value iMsg;
iMsg["action"] = "status_change";
iMsg["watcher"] = sName;
iMsg["status"] = pInfo->emStatus;
iMsg["pid"] = (int)pInfo->nPid;
iMsg["start_time"] = pInfo->GetStartTime();
AppendTail(sName, *pInfo, "\n\n----------- Start -----------\n");
Notify("/ws", iMsg);
}
void Watcher::Stop(const std::string &sName) {
auto it = _mWatcher.find(sName);
if (it == _mWatcher.end() || it->second.emStatus != EStatus::Running) return;
HANDLE hProc = ::OpenProcess(PROCESS_TERMINATE, FALSE, it->second.nPid);
if (hProc == INVALID_HANDLE_VALUE) {
LOG_ERR("Stop %s ... [Failed]", sName.c_str());
return;
}
if (!::TerminateProcess(hProc, 0)) {
::CloseHandle(hProc);
LOG_ERR("Stop %s ... [Failed]. Error: %d", sName.c_str(), ::GetLastError());
return;
}
::CloseHandle(hProc);
CatchExit(sName, it->second, EStatus::Stopped);
LOG_INFO("Stop %s ... [OK]", sName.c_str());
}
void Watcher::StopAll() {
for (auto & kv : _mWatcher) Stop(kv.first);
}
void Watcher::Remove(const std::string &sName) {
auto it = _mWatcher.find(sName);
if (it == _mWatcher.end()) return;
if (it->second.emStatus == EStatus::Running) Stop(sName);
_mWatcher.erase(it);
}
Watcher::Info * Watcher::Get(const std::string &sName) {
auto it = _mWatcher.find(sName);
if (it == _mWatcher.end()) return nullptr;
return &it->second;
}
void Watcher::Notify(const std::string & sScope, const Json::Value & rMsg) {
if (_fNotifier) _fNotifier(sScope, rMsg);
}
void Watcher::Tail(const std::string & sName, Info & rInfo) {
static char pLine[8192] = {0}, pData[8192] = {0};
DWORD nSize = 0, nValid = 0;
if (!::PeekNamedPipe(rInfo.hPipe, NULL, 0, &nSize, &nValid, NULL) || nValid <= 0) {
return;
}
memset(pLine, 0, 8192);
memset(pData, 0, 8192);
bool bOk = ReadFile(rInfo.hPipe, pLine, 8192, &nSize, NULL);
if (bOk && nSize > 0) {
int nRead = 0;
for (int i = 0; i < (int)nSize; ++i) {
if (pLine[i] == 0x1B && pLine[i + 1] == '[') {
while (i < (int)nSize && pLine[i] != 'm') i++;
} else {
pData[nRead] = pLine[i];
++nRead;
}
}
memcpy(rInfo.pTail + rInfo.nTail, pData, nRead);
if (rInfo.nTail + nRead > 8192) {
int nIdx = rInfo.nTail + nRead - 8192;
int i = 0;
for (; nIdx < rInfo.nTail + nRead; ++nIdx) {
if (rInfo.pTail[nIdx] == '\n') {
nIdx++;
break;
}
}
for (; i < rInfo.nTail + nRead - nIdx; ++i) {
rInfo.pTail[i] = rInfo.pTail[i + nIdx];
}
rInfo.pTail[i] = '\0';
rInfo.nTail = i;
} else {
rInfo.nTail += nRead;
}
Json::Value iMsg;
iMsg["action"] = "tail_append";
iMsg["watcher"] = sName;
iMsg["data"] = pData;
Notify("/tail/" + sName, iMsg);
Notify("/tail_all", iMsg);
}
}
void Watcher::AppendTail(const std::string & sName, Info & rInfo, const std::string & sTail) {
int nSize = sTail.size();
memcpy(rInfo.pTail + rInfo.nTail, sTail.data(), nSize);
if (rInfo.nTail + nSize > 8192) {
int nIdx = rInfo.nTail + nSize - 8192;
int i = 0;
for (; nIdx < rInfo.nTail + nSize; ++nIdx) {
if (rInfo.pTail[nIdx] == '\n') {
nIdx++;
break;
}
}
for (; i < rInfo.nTail + nSize - nIdx; ++i) {
rInfo.pTail[i] = rInfo.pTail[i + nIdx];
}
rInfo.pTail[i] = '\0';
rInfo.nTail = i;
} else {
rInfo.nTail += nSize;
}
Json::Value iMsg;
iMsg["action"] = "tail_append";
iMsg["data"] = sTail;
Notify("/tail/" + sName, iMsg);
}
void Watcher::CatchExit(const std::string & sName, Info & rInfo, EStatus emStatus, bool bRetry /* = false */) {
::CloseHandle(rInfo.hPipe);
rInfo.emStatus = emStatus;
rInfo.nPid = -1;
rInfo.nStart = 0;
Json::Value iMsg;
iMsg["action"] = "status_change";
iMsg["watcher"] = sName;
iMsg["status"] = rInfo.emStatus;
iMsg["pid"] = (int)rInfo.nPid;
iMsg["start_time"] = rInfo.GetStartTime();
if (bRetry && rInfo.nRetry > 0) {
rInfo.emStatus = EStatus::Retry;
iMsg["status"] = EStatus::Retry;
Notify("/ws", iMsg);
AppendTail(sName, rInfo, "\n----------- Auto-retry -----------\n");
Start(sName, rInfo.sPath, rInfo.sCmd, rInfo.nRetry - 1);
} else {
Notify("/ws", iMsg);
AppendTail(sName, rInfo, "\n----------- Stopped -----------\n");
}
}
#endif
C++
1
https://gitee.com/love_linger/supervisor.git
git@gitee.com:love_linger/supervisor.git
love_linger
supervisor
supervisor
master

搜索帮助