8 Star 22 Fork 10

leo / supervisor

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
watcher_linux.cc 9.00 KB
一键复制 编辑 原始数据 按行查看 历史
leo 提交于 2019-10-25 17:10 . 增加Windows平台支持
#if !defined(_WIN32)
#include "watcher.h"
#include "logger.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <csignal>
#include <cstring>
#include <cstdio>
#include <memory>
#include <iostream>
#include <sstream>
static int nCpuIdx = -1;
static int nVszIdx = -1;
static int nResIdx = -1;
static void GetKeyIdx() {
static bool bCalc = false;
if (bCalc) return;
bCalc = true;
FILE * pPipe = popen("ps -u -p 1", "r");
if (!pPipe) return;
std::string sResult;
sResult.reserve(4096);
char pBuf[2048] = {0};
while (!feof(pPipe)) {
size_t nSize = fread(pBuf, 1, 2048, pPipe);
if (nSize > 0) sResult.append(pBuf, nSize);
}
std::istringstream issH(sResult);
int nIdx = 0;
while (issH.good()) {
std::string sKey;
issH >> sKey;
if (sKey == "%CPU") nCpuIdx = nIdx;
else if (sKey == "VSZ") nVszIdx = nIdx;
else if (sKey == "RSS") nResIdx = nIdx;
nIdx++;
}
pclose(pPipe);
}
Watcher & Watcher::Instance() {
static std::unique_ptr<Watcher> iIns;
if (!iIns) iIns.reset(new Watcher);
GetKeyIdx();
return *iIns;
}
void Watcher::Breath() {
static time_t nLastBreath = 0;
static char pBuf[2048] = {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) {
int nStatus = 0;
int nChild = waitpid(kv.second.nPid, &nStatus, WNOHANG);
if (nChild > 0) {
Tail(kv.first, kv.second);
CatchExit(kv.first, kv.second, WEXITSTATUS(nStatus) == 0 ? EStatus::Stopped : EStatus::Fatal, true);
continue;
} else if (nChild == 0) {
Tail(kv.first, kv.second);
std::string sCmd = "ps -u -p " + std::to_string(kv.second.nPid);
std::string sResult;
sResult.reserve(1024 * 8);
FILE * pPipe = popen(sCmd.data(), "r");
if (!pPipe) continue;
while (!feof(pPipe)) {
size_t nSize = fread(pBuf, 1, 2048, pPipe);
if (nSize > 0) sResult.append(pBuf, nSize);
}
pclose(pPipe);
int nVsz = 0, nRes = 0, nIdx = 0;
std::string sCpu = "";
std::istringstream iss(sResult);
while (iss.get() != '\n') continue;
while (iss.good()) {
std::string sData;
iss >> sData;
if (nIdx == nCpuIdx) {
sCpu = sData;
} else if (nIdx == nVszIdx) {
nVsz = atoi(sData.data());
} else if (nIdx == nResIdx) {
nRes = atoi(sData.data());
}
nIdx++;
}
Json::Value iStatus;
iStatus["name"] = kv.first;
iStatus["cpu"] = sCpu;
iStatus["vsz"] = nVsz;
iStatus["res"] = nRes;
iMsg["watchers"].append(iStatus);
} else {
CatchExit(kv.first, kv.second, errno == EINTR ? EStatus::Stopped : EStatus::Fatal, true);
}
}
}
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;
}
pInfo->nStart = time(NULL);
std::vector<std::string> vParams;
std::string sDeli = " \t";
std::string::size_type nStart = 0, nEnd = 0;
while (nEnd < sCmd.length()) {
char nTemp = sCmd[nEnd];
if (sDeli.find(nTemp) != std::string::npos) {
if (nStart != nEnd) vParams.push_back(sCmd.substr(nStart, nEnd - nStart));
++nEnd;
nStart = nEnd;
} else {
++nEnd;
}
}
if (nStart != nEnd) vParams.push_back(sCmd.substr(nStart));
if (vParams.size() <= 0) return;
int pFd[2] = {0, 0};
if (pipe(pFd) == -1) {
LOG_ERR("Failed to create pipe for start watcher [%s]", sName.data());
return;
}
pid_t nChildPid = fork();
if (nChildPid == -1) {
LOG_ERR("Start %s ... [Failed] Path : %s, Cmd : %s", sName.data(), sPath.data(), sCmd.data());
pInfo->emStatus = EStatus::Fatal;
pInfo->nPid = -1;
} else if (nChildPid == 0) {
close(pFd[0]);
dup2(pFd[1], STDERR_FILENO);
dup2(pFd[1], STDOUT_FILENO);
setpgid(nChildPid, nChildPid);
if (-1 == chdir(sPath.data())) {
std::cerr << "Failed to change directory : " << sPath << std::endl;
} else {
char * pParam[20] = {0};
memset(pParam, 0, sizeof(pParam));
for (int i = 0; i < vParams.size() && i < 19; ++i)
pParam[i] = (char *)vParams[i].data();
if (-1 == execvp(vParams[0].data(), pParam))
std::cerr << "No such file named : " << vParams[0] << std::endl;
}
exit(-1);
} else {
close(pFd[1]);
int nFlags = fcntl(pFd[0], F_GETFL, 0);
fcntl(pFd[0], F_SETFL, O_NONBLOCK | nFlags);
pInfo->nFd = pFd[0];
pInfo->nPid = nChildPid;
pInfo->emStatus = EStatus::Running;
LOG_INFO("Start %s ... [OK]. FD : %d, PID : %d", sName.c_str(), pInfo->nFd, pInfo->nPid);
}
Json::Value iMsg;
iMsg["action"] = "status_change";
iMsg["watcher"] = sName;
iMsg["status"] = pInfo->emStatus;
iMsg["pid"] = 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;
int nStatus = kill(it->second.nPid, SIGINT);
if (nStatus == -1) {
LOG_ERR("Stop %s ... [Failed]", sName.data());
} else {
nStatus = 0;
Tail(sName, it->second);
while (waitpid(it->second.nPid, &nStatus, 0) == -1) {
if (errno != EINTR) break;
}
CatchExit(sName, it->second, WEXITSTATUS(nStatus) == 0 ? EStatus::Stopped : EStatus::Fatal);
LOG_INFO("Stop %s ... [OK]", sName.data());
}
}
void Watcher::StopAll() {
int nStatus = 0;
for (auto & kv : _mWatcher) {
if (kv.second.emStatus == EStatus::Running) {
kill(kv.second.nPid, SIGINT);
while (waitpid(kv.second.nPid, &nStatus, 0) == -1) {
if (errno != EINTR) break;
}
}
}
}
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};
memset(pLine, 0, 8192);
memset(pData, 0, 8192);
int nSize = read(rInfo.nFd, pLine, 8192);
if (nSize > 0) {
int nRead = 0;
for (int i = 0; i < nSize; ++i) {
if (pLine[i] == 0x1B && pLine[i + 1] == '[') {
while (i < 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 */) {
close(rInfo.nFd);
rInfo.emStatus = emStatus;
rInfo.nFd = -1;
rInfo.nPid = -1;
rInfo.nStart = 0;
Json::Value iMsg;
iMsg["action"] = "status_change";
iMsg["watcher"] = sName;
iMsg["status"] = rInfo.emStatus;
iMsg["pid"] = 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

搜索帮助