.gitignore000066400000000000000000000011601516106473500130540ustar00rootroot00000000000000# Logs logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules jspm_packages # Optional npm cache directory .npm # Optional REPL history .node_repl_history systemd-watchdog-notify.iws /build /dev.json .scrutinizer.yml000066400000000000000000000005451516106473500142540ustar00rootroot00000000000000checks: javascript: true filter: excluded_paths: - test/* - node_modules/* - build/* - docs/* build: environment: node: 7 dependencies: before: - npm install -g grunt-cli tests: override: - command: 'grunt' coverage: file: 'build/coverage/clover.xml' format: 'clover' .travis.yml000066400000000000000000000001321516106473500131730ustar00rootroot00000000000000language: node_js node_js: - "7" - "node" before_script: - npm install grunt-cli -g Gruntfile.js000066400000000000000000000021711516106473500133640ustar00rootroot00000000000000const builder = require('corifeus-builder'); module.exports = (grunt) => { const loader = new builder.Loader(grunt); loader.js(); grunt.config.set('cory-replace', { header: { header: true, replace: ` [![Build Status](https://travis-ci.org/patrikx3/\${git.repo}.svg?branch=master)](https://travis-ci.org/patrikx3/\${git.repo}) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/patrikx3/\${git.repo}/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/patrikx3/\${git.repo}/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/patrikx3/\${git.repo}/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/patrikx3/\${git.repo}/?branch=master) [![Trello](https://img.shields.io/badge/Trello-p3x-026aa7.svg)](https://trello.com/b/gqKHzZGy/p3x) `, files: [ '*.md' ] }, footer: { footer: true, replace: `[by Patrik Laszlo](http://patrikx3.tk)`, files: [ '*.md' ] } }) grunt.registerTask('default', builder.config.task.build.js); }LICENSE000066400000000000000000000021351516106473500120740ustar00rootroot00000000000000MIT License Copyright (c) 2017 Patrik Laszlo Corifeus http://patrikx3.tk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.README.md000066400000000000000000000042241516106473500123470ustar00rootroot00000000000000[//]: #@corifeus-header [![Build Status](https://travis-ci.org/patrikx3/systemd-watchdog-notify.svg?branch=master)](https://travis-ci.org/patrikx3/systemd-watchdog-notify) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/patrikx3/systemd-watchdog-notify/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/patrikx3/systemd-watchdog-notify/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/patrikx3/systemd-watchdog-notify/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/patrikx3/systemd-watchdog-notify/?branch=master) [![Trello](https://img.shields.io/badge/Trello-p3x-026aa7.svg)](https://trello.com/b/gqKHzZGy/p3x) [//]: #corifeus-header:end # SystemD Watchdog Notify This notifies changes in the SystemD via e-mail. Right now it polls, so that it gets all changes. It task about 30-50 milliseconds per run on my 3.3 GHz Pentium 2 cores, it is nothing instead tons of functions. All automatic, requires email and a few tweaks as you want. ## Using terminal ```bash git clone https://github.com/patrikx3/systemd-watchdog-notify.git cd systemd-watchdog-notify ./watchdog ./watchdog.json ``` ## Settings ```types```: Array, can be empty, actual ```man systemctl``` type. ```nodemailer.config```: Exact nodemailer config, any of that. ```interval, ping```: Uses npm ```milliseconds``` framework for turn into actual milliseconds from a string. ```json { "excluded-pattern": "is a RegExp, you can omit this or exclude actually", "service": "p3x-watchdog", "interval": "1 minute", "ping": "2 hours", "types": [ "service" ], "mail": { "prefix": "P3X-WATCHDOG" }, "email": { "to": "try@me.tk", "from": "me@with.you" }, "nodemailer": { "config": { "host": "mail.server.org", "port": 465, "secure": true, "auth": { "user": "username", "pass": "password" } } } } ``` ## Using from code ```javascript const Watchdog = require('systemd-watchdog-notify'); const settings = require('./watchdog.json'); const watchdog = Watchdog(settings); watchdog.run(); ``` [//]: #@corifeus-footer [by Patrik Laszlo](http://patrikx3.tk) [//]: #@corifeus-footer:endindex.js000066400000000000000000000000421516106473500125270ustar00rootroot00000000000000module.exports = require('./src');package.json000066400000000000000000000017031516106473500133550ustar00rootroot00000000000000{ "name": "systemd-watchdog-notify", "corifeus": { "time": "2/21/2017, 5:56:23 PM" }, "version": "1.0.13-9", "description": "SystemD Watchdog Notify", "main": "index.js", "dependencies": { "millisecond": "^0.1.2", "moment": "^2.17.1", "nodemailer": "^3.1.3" }, "devDependencies": { "corifeus-builder": "^1.0.216-136" }, "scripts": { "test": "grunt mocha_istanbul" }, "repository": { "type": "git", "url": "git+https://github.com/patrikx3/systemd-watchdog-notify.git" }, "keywords": [ "systemd", "systemd-service", "watchdog", "notify", "linux" ], "author": "Patrik Laszlo ", "license": "MIT", "bugs": { "url": "https://github.com/patrikx3/systemd-watchdog-notify/issues" }, "homepage": "https://github.com/patrikx3/systemd-watchdog-notify#readme" }src/000077500000000000000000000000001516106473500116555ustar00rootroot00000000000000src/index.js000066400000000000000000000145731516106473500133340ustar00rootroot00000000000000const exec = require('child_process').exec; const ms = require('millisecond'); const Mail = require('./mail') const moment = require('moment'); module.exports = (settings) => { const mail = Mail(settings); const parseRow = /^([^\s]+)([\s]+)([^\s]+)([\s]+)([^\s]+)([\s]+)([^\s]+)([\s]+)(.+)$/; //const command = 'systemctl -all --full --plain --no-pager --no-legend '; let types = ''; if (settings.types.length > 0) { types = `--type=${settings.types.join(',')}`; } const options = settings.options || ''; const command = `systemctl --plain --no-pager --no-legend ${options} ${types}`; // systemctl --state=not-found --all let db; let timeLastPing; let timePing = ms(settings.ping); let timePingString = settings.ping; let interval = ms(settings.interval) let intervalString = settings.interval; const showStatus = () => { if (settings.types.length === 0 ) { status(`watchdog all`) } else { status(`watchdog type(s): ${settings.types.join(',')}`) } status(`ping: ${timePingString}`); status(`interval: ${intervalString}`); status(`command: ${command}`); } const status = (message) => { console.log(`${settings.mail.prefix}: ${message}`); }; status(`started`); if (settings.test) { interval = 3000; intervalString = interval + ' ms'; timePing = 3000; timePingString = 3000 + ' ms'; status(`TEST MODE`); } showStatus(); /** * Parses systemctl services * @param input * @returns {{}} */ const parseStatus = (input) => { const lines = input.split("\n"); lines.pop(); const dbUpdate = {}; lines.forEach((line) => { const [, unit, , load, , active , , sub, , description ]= parseRow.exec(line); dbUpdate[unit] = { unit: unit, load: load, active: active, sub: sub, description: new String(description).trim() }; }) return dbUpdate; } /** * Runs an update of getting current services and parses and returns at once. * @returns {Promise} */ const update = () => { return new Promise((resolve, reject) => { exec(command, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => { if (error) { reject(error); return; } if (stderr !== '') { reject({ stderr: stderr, stout: stdout, }); return; } resolve(parseStatus(stdout)); }); }) } /** * Simple clone program * @param obj */ const clone = (obj) => { return JSON.parse(JSON.stringify(obj)); } let excludePattern; const excluded = (isExcludable) => { if (settings['excluded-pattern'] === undefined) { return false; } else if (excludePattern === undefined) { excludePattern = new RegExp(settings['excluded-pattern']) } const excludedResult = excludePattern.test(isExcludable.unit); if (excludedResult) { status('excluded unit') //console.log(isExcludable); } return excludedResult; } /** * The watch program. */ const watch = () => { update() .then((dbUpdate) => { if (db === undefined) { db = dbUpdate; /* const notFound = []; Object.keys(db).forEach((key) => { if (db[key].load == 'not-found') { notFound.push(db[key]); } }) if (notFound.length > 0) { mail.system.send(`${settings.mail.prefix}: NOT FOUND`, notFound); } */ return; } const watchUpdate = { changes: {}, added: {}, removed: {} } Object.keys(dbUpdate).forEach((unit) => { if (db[unit] === undefined && !excluded(dbUpdate[unit])) { watchUpdate.added[unit] = clone(dbUpdate[unit]); } else if(JSON.stringify(db[unit]) !== JSON.stringify(dbUpdate[unit]) && !excluded(dbUpdate[unit])) { watchUpdate.changes[unit] = { 'old': clone(db[unit]), 'new': clone(dbUpdate[unit]), }; } delete db[unit]; }); watchUpdate.removed = {}; Object.keys(db).forEach((unit) => { if (!excluded(db[unit])) { watchUpdate.removed[unit] = clone(db[unit]); } }) db = dbUpdate; let sendChanges = false; Object.keys(watchUpdate).forEach((watchUpdateKey) => { if (Object.keys(watchUpdate[watchUpdateKey]).length > 0) { sendChanges = true; } }) if (sendChanges === true) { status('new update found - sending e-mail') watchUpdate.moment = moment().format(settings.moment); mail.system.send(`${settings.mail.prefix}: CHANGED`, watchUpdate ); } }) .catch((error) => { console.error(error); mail.system.send(`${settings.mail.prefix}: ERROR`, JSON.stringify(error, ["message", "arguments", "type", "name"], 2) ); }) .then(() => { if (timeLastPing == undefined || Date.now() - timeLastPing > timePing ) { status(`ping - ${Object.keys(db).length} items - every ${timePingString}`); //console.log(db); timeLastPing = Date.now(); } }) } return { run: () => { watch(); setInterval(watch, interval); } } } src/mail.js000066400000000000000000000022501516106473500131340ustar00rootroot00000000000000const nodemailer = require('nodemailer'); const os = require('os'); module.exports = (settings) => { const transporter = nodemailer .createTransport(settings.nodemailer.config); transporter.verify() .then((result) => { console.log('Mail is working.'); }) .catch((error) => { console.error('Mail is not working.', error); }); const send = (from, to, subject, body) => { if (typeof body !== 'string') { body = JSON.stringify(body, null, 2); } const message = { from: from, to: to, subject: subject, text: body }; console.log(message); transporter.sendMail(message) .then((info) => { console.log(info); }) .catch((error) => { console.error(error); }); } const system = { send: (subject, body) => { subject = `${os.hostname()} - ${subject}`; send(settings.email.from, settings.email.to, subject, body ); } } return { send: send, system: system }; } systemd-watchdog-notify.iml000066400000000000000000000013271516106473500163700ustar00rootroot00000000000000 systemd-watchdog-notify.ipr000066400000000000000000000075001516106473500164000ustar00rootroot00000000000000 false false false Android watchdog000077500000000000000000000004611516106473500126150ustar00rootroot00000000000000#!/usr/bin/env node const process = require('process'); if (process.argv.length < 3) { console.log(` Please use an argument for the settings. For example: ./watchdog ./dev.json `); return; } const Watchdog = require('./index'); const watchdog = Watchdog(require(process.argv[2])); watchdog.run();