domotz-remote-pawn
Version:
Domotz Agent
167 lines (159 loc) • 6.44 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2016 Domotz Ltd
*
* Domotz Agent 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.
*
* Domotz Agent 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.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
* Created by Iacopo Papalini <iacopo@domotz.com> on 07/12/16.
*
* This module spawns a process that checks periodically the responsiveness
* of the Domotz Agent and kills it if is not responding over HTTP over socket
* requests in a sensible time.
*
* To be sure that only one instance of watcher is running we need:
*
* gracePeriod > pollPeriod
*/
var MODULE = 'DOMOTZ_STALL_WATCHER';
module.exports.init = function (resourceLocator) {
var env = process.env;
var os = resourceLocator.os;
var path = resourceLocator.path;
var fs = resourceLocator.fs;
var childProcess = resourceLocator.utils.childProcess;
var watcherConf = resourceLocator.settingsManager.getStallWatcherSettings();
var pollPeriod = parseInt(env.DOMOTZ_POLL_PERIOD || 60);
var retryPeriod = parseInt(env.DOMOTZ_RETRY_PERIOD || 10);
var retryNumber = parseInt(env.DOMOTZ_RETRY_NUMBER || 11);
var gracePeriod = parseInt(env.DOMOTZ_GRACE_PERIOD || 120) * 1000;
var flushLogFailure = 6;
var myConsole = resourceLocator.log.decorateLogs();
var fileName = path.join(os.tmpdir(), MODULE.toLocaleLowerCase() + '.sh');
var pidFileName = path.join(os.tmpdir(), MODULE.toLocaleLowerCase() + '.pid');
var logFileName = path.join(env.DOMOTZ_LOG_DIR, MODULE.toLocaleLowerCase() + '.log');
var terminateStallWatcher = function () {
if (fs.existsSync(pidFileName)) {
var pid = parseInt(fs.readFileSync(pidFileName, 'utf8').trim(), 10);
if (pid && !isNaN(pid)) {
try {
process.kill(pid, 'SIGTERM');
myConsole.info('Stall watcher with PID %s killed', pid);
} catch (e) {
myConsole.error('Error killing stall watcher with PID %s: %s', pid, e.message);
}
} else {
myConsole.warn('Invalid PID in file %s', pidFileName);
}
fs.unlinkSync(pidFileName);
}
};
var contents =
'#!/bin/sh\n' +
'LOG_FILE=' +
logFileName +
'\n' +
'period=' +
pollPeriod +
'\n' +
'parent_pid=' +
process.pid +
'\n' +
'failure=0\n' +
'echo `date` "$0 start with pid $$ to monitor listener with pid $parent_pid." >> $LOG_FILE\n' +
'while true\n' +
'do\n' +
'\tsleep ${period}\n' +
'\tcurrent_listener_pid=$(cat ' +
env.LISTENER_PID_FILE +
')\n' +
'\tif [ $current_listener_pid -ne $parent_pid ]; then\n' +
'\t\techo `date` "Parent listener is died [pid: $parent_pid]. Current instance is running with pid $current_listener_pid. Exit..." >> $LOG_FILE\n' +
'\t\texit 0\n' +
'\tfi\n' +
'\tdomotz_curl --fail --silent -X POST -A domotz-zabbix-proxy -d \'{"action": "listener_status_check"}\' --unix-socket ' +
env.DOMOTZ_HTTP_ON_UNIX_SOCKET_FILE +
' --max-time ' +
watcherConf.timeout +
' http://localhost\n' +
'\tif [ $? -ne 0 ]; then\n' +
'\t\tfailure=$((failure+1))\n' +
'\t\tif [ $failure -eq ' +
retryNumber +
' ]; then\n' +
'\t\t\techo `date` "Killing not responding listener process; pid: $parent_pid" >> $LOG_FILE\n' +
'\t\t\tkill -9 $parent_pid\n' +
'\t\t\texit 1\n' +
'\t\telse\n' +
'\t\t\techo `date` "Not responding listener process; pid: $parent_pid - tentative $failure" >> $LOG_FILE\n' +
'\t\t\tif [ $failure -eq ' +
flushLogFailure +
' ]; then\n' +
'\t\t\t\tkill -1 $parent_pid\n' +
'\t\t\tfi\n' +
'\t\t\tperiod=' +
retryPeriod +
'\n' +
'\t\tfi\n' +
'\telse\n' +
'\t\tperiod=' +
pollPeriod +
'\n' +
'\t\tfailure=0\n' +
'\tfi\n' +
'done\n';
return {
start: function () {
'use strict';
if (process.platform === 'win32') {
myConsole.info('Stall watcher disabled on windows');
return;
}
if (!watcherConf.enabled) {
myConsole.info('Stall watcher disabled by settings');
return;
}
terminateStallWatcher();
myConsole.info(
'Start process: [%s]. Grace Time: %d s. ' + 'Poll Period: %d s. Retry Period: %d s. #Retry: %d',
fileName,
gracePeriod / 1000,
pollPeriod,
retryPeriod,
retryNumber
);
setTimeout(function () {
fs.writeFileSync(fileName, contents, { mode: '0700' });
var watcher = childProcess.spawn(fileName, [], {
env: process.env,
});
if (watcher.pid) {
myConsole.info('Stall watcher started with PID %s', watcher.pid);
}
fs.writeFileSync(pidFileName, watcher.pid.toString(), { mode: '0644' });
watcher.on('error', function (err) {
myConsole.error('Unable to start stall watcher %s', err.toString());
});
watcher.on('exit', function (code) {
if (code !== 0) {
myConsole.error('Exit with code %s for stall watcher.', code);
} else {
myConsole.warn('Graceful exit for stall watcher.');
}
process.exit(0);
});
}, gracePeriod);
},
kill: function () {
terminateStallWatcher();
},
};
};