RSS Git Download  Clone
Raw Blame History
const 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);
        }
    }

}