6ffe80b672
Added a signal handler that will print out signal details upon receiving them. This should give up some hint to the cause of crashes for debugging reasons. The root index web page will now only be updated once. Hopefully this reduces chance of multiple instances clashing with each other.
306 lines
8.3 KiB
C++
Executable File
306 lines
8.3 KiB
C++
Executable File
// This file is part of Motion Watch.
|
|
|
|
// Motion Watch is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Motion Watch is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
#include "mo_detect.h"
|
|
#include "logger.h"
|
|
|
|
void detectMoInFile(const string &detPath, const string &recPath, shared_t *share)
|
|
{
|
|
detLog("detect_mo_in_file() -- start", share);
|
|
|
|
Mat thumbNail;
|
|
|
|
if (moDetect(detPath, thumbNail, share))
|
|
{
|
|
share->skipCmd = true;
|
|
|
|
wrOut(recPath, thumbNail, share);
|
|
}
|
|
|
|
if (exists(detPath)) remove(detPath);
|
|
if (exists(recPath)) remove(recPath);
|
|
|
|
detLog("detect_mo_in_file() -- finished", share);
|
|
}
|
|
|
|
static void *runCmd(void *arg)
|
|
{
|
|
auto args = static_cast<cmdRes_t*>(arg);
|
|
|
|
args->finished = false;
|
|
args->ret = 0;
|
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
|
|
|
args->ret = pclose(popen(args->cmd.c_str(), "r"));
|
|
args->finished = true;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool allCmdsFinished(const vector<cmdRes_t*> &resList)
|
|
{
|
|
for (auto &&res : resList)
|
|
{
|
|
if (!res->finished) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void cancelAllCmds(const vector<cmdRes_t*> &resList)
|
|
{
|
|
for (auto &&res : resList)
|
|
{
|
|
if (!res->finished)
|
|
{
|
|
pthread_cancel(res->thr);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool allCmdsDidNotFail(const vector<cmdRes_t*> &resList)
|
|
{
|
|
for (auto &&res : resList)
|
|
{
|
|
if (res->ret != 0) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void cleanupRes(vector<cmdRes_t*> &resList)
|
|
{
|
|
for (auto &&res : resList)
|
|
{
|
|
delete res;
|
|
}
|
|
}
|
|
|
|
void runTOCmds(int timeOut, const vector<string> &cmds, vector<cmdRes_t*> &resList, bool abs = true)
|
|
{
|
|
resList.clear();
|
|
|
|
for (auto &&cmd : cmds)
|
|
{
|
|
auto res = new struct cmdRes_t;
|
|
|
|
res->cmd = cmd;
|
|
res->finished = false;
|
|
res->ret = pthread_create(&res->thr, NULL, &runCmd, res);
|
|
|
|
resList.push_back(res);
|
|
}
|
|
|
|
if (abs)
|
|
{
|
|
sleep(timeOut);
|
|
|
|
if (!allCmdsFinished(resList))
|
|
{
|
|
sleep(2);
|
|
}
|
|
|
|
cancelAllCmds(resList);
|
|
}
|
|
else
|
|
{
|
|
for (auto i = 0; !allCmdsFinished(resList); ++i)
|
|
{
|
|
sleep(1);
|
|
|
|
if (i >= timeOut)
|
|
{
|
|
cancelAllCmds(resList); break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void runTOCmd(int timeout, const string &cmd, bool abs = true)
|
|
{
|
|
vector<string> cmds;
|
|
vector<cmdRes_t*> rets;
|
|
|
|
cmds.push_back(cmd);
|
|
|
|
runTOCmds(timeout, cmds, rets, abs);
|
|
cleanupRes(rets);
|
|
}
|
|
|
|
void recLoop(shared_t *share)
|
|
{
|
|
while (rdConf(share))
|
|
{
|
|
enforceMaxLogSize(share->recLogPath, share);
|
|
enforceMaxLogSize(share->detLogPath, share);
|
|
|
|
initLogFile(share->recLogPath, share->recLogFile);
|
|
initLogFile(share->detLogPath, share->detLogFile);
|
|
|
|
initLogFrontPages(share);
|
|
|
|
recLog("rec_loop() -- start", share);
|
|
|
|
if (!exists("/tmp/mow-lock") && share->updateRoot)
|
|
{
|
|
system("touch /tmp/mow-lock");
|
|
|
|
genCSS(share);
|
|
genHTMLul(share->webRoot, string(APP_NAME) + " " + string(APP_VER), share);
|
|
|
|
remove("/tmp/mow-lock");
|
|
recLog("webroot page updated: " + cleanDir(share->webRoot) + "/index.html", share);
|
|
|
|
share->updateRoot = false;
|
|
}
|
|
|
|
genHTMLul(share->outDir, share->camName, share);
|
|
|
|
recLog("camera specific webroot page updated: " + share->outDir + "/index.html", share);
|
|
|
|
for (auto i = 0; i < share->numOfClips; ++i)
|
|
{
|
|
auto detPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->detSuffix;
|
|
auto recPath = cleanDir(share->buffDir) + "/" + to_string(i) + share->recSuffix;
|
|
|
|
vector<string> cmds;
|
|
vector<cmdRes_t*> rets;
|
|
|
|
if (share->recordUrl == share->detectUrl)
|
|
{
|
|
recPath = detPath;
|
|
}
|
|
else
|
|
{
|
|
cmds.push_back("ffmpeg -hide_banner -i " + share->recordUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + recPath);
|
|
}
|
|
|
|
cmds.push_back("ffmpeg -hide_banner -i " + share->detectUrl + " -y -vcodec " + share->vidCodec + " -t " + to_string(share->clipLen) + " " + detPath);
|
|
|
|
recLog("fetching camera footage -- ", share);
|
|
|
|
runTOCmds(share->clipLen, cmds, rets);
|
|
|
|
if (allCmdsDidNotFail(rets))
|
|
{
|
|
new thread(detectMoInFile, detPath, recPath, share);
|
|
}
|
|
else
|
|
{
|
|
recLog("one or both fetch cmds failed.", share);
|
|
}
|
|
|
|
recLog("fetch results:", share);
|
|
|
|
for (auto &&ret : rets)
|
|
{
|
|
recLog("cmd: " + ret->cmd + " return_code: " + to_string(ret->ret), share);
|
|
}
|
|
}
|
|
|
|
if (!share->skipCmd)
|
|
{
|
|
recLog("no motion detected", share);
|
|
|
|
if (share->postCmd.empty())
|
|
{
|
|
recLog("post command not defined, skipping.", share);
|
|
}
|
|
else
|
|
{
|
|
recLog("running post command: " + share->postCmd, share);
|
|
runTOCmd(14, share->postCmd, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
recLog("motion detected, skipping the post command.", share);
|
|
}
|
|
|
|
recLog("rec_loop() -- finished", share);
|
|
|
|
if (share->retCode != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void sigHandler(int signal, siginfo_t *info, void *context)
|
|
{
|
|
cerr << "received signal details: " << endl;
|
|
cerr << "-- si_pid: " << info->si_pid << endl;
|
|
cerr << "-- si_signo: " << info->si_signo << endl;
|
|
cerr << "-- si_code: " << info->si_code << endl;
|
|
cerr << "-- si_errno: " << info->si_errno << endl;
|
|
cerr << "-- si_uid: " << info->si_uid << endl;
|
|
cerr << "-- si_addr: " << info->si_addr << endl;
|
|
cerr << "-- si_status: " << info->si_status << endl;
|
|
cerr << "-- si_band: " << info->si_band << endl;
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
struct shared_t sharedRes;
|
|
|
|
struct sigaction act = { 0 };
|
|
|
|
act.sa_flags = SA_SIGINFO;
|
|
act.sa_sigaction = &sigHandler;
|
|
|
|
sigaction(SIGTERM, &act, NULL);
|
|
sigaction(SIGABRT, &act, NULL);
|
|
sigaction(SIGSEGV, &act, NULL);
|
|
sigaction(SIGILL, &act, NULL);
|
|
sigaction(SIGFPE, &act, NULL);
|
|
sigaction(SIGBUS, &act, NULL);
|
|
|
|
sharedRes.conf = parseForList("-c", argc, argv);
|
|
|
|
if (parseForParam("-h", argc, argv, true) == "true")
|
|
{
|
|
cout << "Motion Watch " << APP_VER << endl << endl;
|
|
cout << "Usage: mow <argument>" << endl << endl;
|
|
cout << "-h : display usage information about this application." << endl;
|
|
cout << "-c : path to a config file." << endl;
|
|
cout << "-v : display the current version." << endl << endl;
|
|
cout << "note: multiple -c config files can be passed, reading from left" << endl;
|
|
cout << " to right. any conflicting values between the files will" << endl;
|
|
cout << " have the latest value from the latest file overwrite the" << endl;
|
|
cout << " the earliest." << endl;
|
|
}
|
|
else if (parseForParam("-v", argc, argv, true) == "true")
|
|
{
|
|
cout << APP_VER << endl;
|
|
}
|
|
else if (sharedRes.conf.empty())
|
|
{
|
|
cerr << "err: no config file(s) were given in -c" << endl;
|
|
}
|
|
else
|
|
{
|
|
sharedRes.retCode = 0;
|
|
sharedRes.updateRoot = true;
|
|
sharedRes.skipCmd = false;
|
|
sharedRes.init = true;
|
|
|
|
recLoop(&sharedRes);
|
|
|
|
return sharedRes.retCode;
|
|
}
|
|
|
|
return EINVAL;
|
|
}
|