#!/bin/bash # Run this file # bash <(wget -qO- -o- https://gist.githubusercontent.com/mikeytown2/f2e4e5b238f15d14afc336e137bb37ab/raw/cato_mnmon.sh) # bash -c "$(wget -qO- -o- goo.gl/CSPC2T)" # Only run if root. if [ "$(whoami)" != "root" ] && [ "$(whoami)" != "pi" ] then echo "Script must be run as user: root" echo "To switch to the root user type" echo echo "sudo su" echo echo "And then re-run this command." exit -1 fi # Check for systemd systemctl --version >/dev/null 2>&1 || { echo "systemd is required. Are you using Ubuntu 16.04?" >&2; exit 1; } # Start in home dir cd ~/ cat << "CATO_MNMON" __ __ / _ |_ _ / _ . _ \__(_||_(_)\__(_)|| ) __ | \. _ _ _ _ _| |__/|_)(_(_)| (_| |\/| _ _ .|_ _ _ | |(_)| )||_(_)| CATO_MNMON # Get new user cato_mnmon. echo USRNAME="cato_mnmon" # if id "$USRNAME" >/dev/null 2>&1; then # echo # else # read -e -i "$USRNAME" -p "Username (lowercase): " input # USRNAME="${input:-$USRNAME}" # fi # Convert to lowercase. USRNAME=$(echo "${USRNAME}" | awk '{print tolower($0)}') # Get webhook url. echo -n 'Get Webhook URL: Your personal server (press plus on left if you do not have one)' echo -n ' -> text channels, general, click gear to "edit channel" -> Left side select Webhooks' echo -n ' -> Create Webhook -> Copy webhook url -> save' echo echo 'This webhook will be used for info pings every 2 hours.' echo WEBHOOKURL="" if [ -f "/home/$USRNAME/discord_webhook/cato_mnmon.js" ] then WEBHOOKURL=`cat /home/$USRNAME/discord_webhook/cato_mnmon.js | grep "const URL =" | cut -d "'" -f2 ` fi while : do read -e -i "$WEBHOOKURL" -p "Info WebHook URL: " input WEBHOOKURL="${input:-$WEBHOOKURL}" # Get jq. if ! [ -x "$(command -v jq)" ] then sudo apt-get install -y jq fi TOKEN="`wget -qO- -o- $WEBHOOKURL | jq -r '.token'`" if [ -z "$TOKEN" ]; then echo "Given URL is not a webhook." echo echo -n 'Get Webhook URL: Your personal server (press plus on left if you do not have one)' echo -n ' -> Right click on your server -> Server Settings -> Webhooks' echo -n ' -> Create Webhook -> Copy webhook url -> save' echo else break fi done # Get webhook alert url. echo echo echo -n 'Get Webhook URL: Your personal server (press plus on left if you do not have one)' echo -n ' -> text channels, general, click gear to "edit channel" -> Left side select Webhooks' echo -n ' -> Create Webhook -> Copy webhook url -> save' echo echo 'This webhook will be used for important alerts.' echo 'You can reuse the same webhook url if you want all info and alerts in the same channel.' echo WEBHOOKALERTURL="" if [ -f "/home/$USRNAME/discord_webhook/cato_mnmon.js" ] then WEBHOOKALERTURL=`cat /home/$USRNAME/discord_webhook/cato_mnmon.js | grep "const URL_ALERT =" | cut -d "'" -f2 ` fi while : do read -e -i "$WEBHOOKALERTURL" -p "Alert WebHook URL: " input WEBHOOKALERTURL="${input:-$WEBHOOKALERTURL}" # Get jq. if ! [ -x "$(command -v jq)" ] then sudo apt-get install -y jq fi TOKEN="`wget -qO- -o- $WEBHOOKALERTURL | jq -r '.token'`" if [ -z "$TOKEN" ]; then echo "Given URL is not a webhook." echo echo -n 'Get Webhook URL: Your personal server (press plus on left if you do not have one)' echo -n ' -> Right click on your server -> Server Settings -> Webhooks' echo -n ' -> Create Webhook -> Copy webhook url -> save' echo else break fi done echo sudo apt-get update # Make sure pwgen is installed. if ! [ -x "$(command -v pwgen)" ] then sudo apt-get install -yq pwgen fi # Create username. if [ -d "/home/$USRNAME" ] then systemctl stop $USRNAME userdel $USRNAME fi sudo useradd -m $USRNAME -s /bin/bash # Make sure dir exists. if [ ! -d "/home/$USRNAME" ] then mkdir -p /home/$USRNAME/ sudo chown -R $USRNAME:$USRNAME /home/$USRNAME/ fi # Set new user cato_mnmon password to a big string. USERPASS=`pwgen -1 -s 44` && echo "$USRNAME:$USERPASS" | chpasswd # Install nodejs. if ! [ -x "$(command -v npm)" ] then bash <(wget -qO- -o- https://deb.nodesource.com/setup_8.x) sudo apt-get install -y nodejs fi # Upgrade if needed. if [[ $(node -v) != v8.1* ]] then bash <(wget -qO- -o- https://deb.nodesource.com/setup_8.x) sudo apt-get install -y nodejs fi # Update npm. npm i -g npm # Create nodejs project. mkdir ~/discord_webhook cd ~/discord_webhook npm init -y if [ ! -d "~/discord_webhook/minimist" ] then npm install --quiet minimist fi if [ ! -d "~/discord_webhook/discord.js" ] then npm install --quiet discord.js fi if [ ! -d "~/discord_webhook/webhook-discord" ] then npm install --quiet webhook-discord fi if [ ! -d "~/discord_webhook/finis" ] then npm install --quiet finis fi # Create project file. touch cato_mnmon.js printf " // Webhook URLs const URL = '${WEBHOOKURL}'; const URL_ALERT = '${WEBHOOKALERTURL}'; " > ~/discord_webhook/cato_mnmon.js ( cat << "CATO_MNMON" // Require the discord.js module const Discord = require('discord.js'); // Create a new Discord client const Bot = new Discord.Client(); // Allow for system calls. const { exec } = require('child_process'); // Create webhook object. const Webhook = require('webhook-discord'); // Create file system object. const fs = require('fs'); // Create finis object. const finis = require('finis') // Get user args const args = require('minimist')(process.argv.slice(2)); // Set variables. const mon_name = 'CatoCoin MN Monitor'; const filename_self = '.*cato_mnmon\.js'; const daemon_name = 'CatoCoin'; const daemon_bin = 'catocoind'; const daemon_cli = 'catocoin-cli'; const daemon_grep = '[c]atocoind'; const daemon_dir_1 = '/home/${mn_username}/.local/bin/'; const daemon_dir_root_1 = '/usr/local/bin/'; const daemon_data_dir_1 = '.catocoin2'; // State variables. global.blockcount = []; global.bad_status = []; global.bad_users = []; global.connection_count = []; global.winner = []; global.mn_winners = []; global.usercount = 0; global.stdout_block_count = []; global.users_tracked = []; global.exec_tracked = []; global.last_logins = []; global.login_pids = []; global.public_ip = 0; global.shutdown_counter = 0; // Catch termination code. finis((code, signal, error) => { global.shutdown_counter++; if (global.shutdown_counter < 2) { send_chat('err', `Monitor shutting down! \nExit Code: ${code} \nSignal: ${signal} \nError: ${error}`, 'ERROR', 'EXIT'); // Wait 3 seconds for the shutdown message to hit discord. setTimeout(() => process.exit(), 3000); } }); // Stop Command. if (args._.indexOf('stop') !== -1 || args.stop) { exec(`ps -ax | grep '[n]ode ${filename_self} .*--daemon' | awk '{ print $1 }'`, (err, pid_to_kill, stderr) => { pid_to_kill = pid_to_kill.trim(); if (pid_to_kill > 1) { console.log(`01 Going to stop ${daemon_name} masternode monitor. ${pid_to_kill}`); exec(`kill ${pid_to_kill}`, (err, pid_to_kill, stderr) => { console.log(`02 ${daemon_name} masternode monitor has stopped.`); process.exit(); }); } else { console.log('03 Monitor not running.'); process.exit(); } }); } // Only allow one daemon to run at a time. exec(`ps -aux | grep "[n]ode ${filename_self} .*--daemon" | wc -l`, (err, already_running, stderr) => { already_running = already_running.trim(); if (already_running > 1 && args._.indexOf('stop') === -1 && !args.stop) { console.log(`11 ${daemon_name} masternode monitor is already running.`); process.exit(); } }); // Get public and private ips. exec('wget -qO- -o- ipinfo.io/ip', (err, public_ip, stderr) => { public_ip = public_ip.trim(); if (args.debug) { console.log(`21 public ip: ${public_ip}`); } exec('ip route get 8.8.8.8 | sed \'s/ uid .*//\' | awk \'{print $NF; exit}\'', (err, private_ip, stderr) => { private_ip = private_ip.trim(); if ( public_ip != private_ip ) { public_ip += ' ' + private_ip; } global.public_ip = public_ip.trim(); if (args.debug) { console.log(`22 private ip: ${private_ip}`); } send_chat('info', `Monitor started` , ''); // Always do a simple check on start. check_logins(); info_on_masternodes(); check_masternodes(); if (args.daemon) { // Check every 10 seconds the status users logged in or out. var logintimer = 10; if (args.logintimer) { var logintimer = args.logintimer; } setInterval(check_logins, logintimer*1000); // Check every 6 seconds the status of all running masternodes. var mntimer = 6; if (args.mntimer) { var mntimer = args.mntimer; } setInterval(check_masternodes, mntimer*1000); // Check every 60 seconds what masternodes are being checked. var infotimercheck = 60; if (args.infotimercheck) { var infotimercheck = args.infotimercheck; } setInterval(info_on_masternodes, infotimercheck*1000); } }); }); function check_logins() { // Get last users that have logged in. exec('last -i -x -F -n 20 | head -n -2', (err, stdout_last_logins, stderr) => { var stdout_last_logins_array = stdout_last_logins.trim().split(/\r?\n/) if (global.last_logins === undefined || global.last_logins.length == 0) { global.last_logins = stdout_last_logins_array; } var difference = global.last_logins.filter(x => !stdout_last_logins_array.includes(x)); if (args.debug) { console.log('31 Users that have logged in/out:' + JSON.stringify(difference, null, 1)); } if (difference.length != 0) { send_chat('warn', `User login/logout change detected \nMessage: ` + JSON.stringify(difference, null, 1), 'WARNING'); } global.last_logins = stdout_last_logins_array; }); // exec(`ps axo ruser:20,rgroup:15,thcount,pid,tty,command | grep -E -i 'tty|pts|dbus|lxsession|lxde' | grep -v -E 'ps axo|grep -E -i tty'`, (err, stdout_login_processes, stderr) => { // var stdout_login_processes_array = stdout_login_processes.trim().split(/\r?\n/) // // console.log(stdout_login_processes); // if (global.login_pids === undefined || global.login_pids.length == 0) { // global.login_pids = stdout_login_processes_array; // } // var difference = global.login_pids.filter(x => !stdout_login_processes_array.includes(x)); // console.log(difference); // global.login_pids = stdout_login_processes_array; // }); } function check_masternodes() { // Get running masternodes. exec(`ps axfo etimes,user:80,command | grep "${daemon_grep}" | grep -v "bash" | awk \'$1 > 30\' | awk \'{ print $2 " " $3}\'`, (err, stdout_users, stderr) => { console.log(`40 ps axfo: ${stdout_users}`); // Extract users and exec paths. stdout_users = stdout_users.trim().split(/\r?\n/).sort(); var users = []; var running_daemon_exec = []; stdout_users.forEach( function (user_exec) { var temp = user_exec.trim().split(' '); var mn_process = temp.pop(); var mn_username = temp.pop(); if (!fs.existsSync(mn_process)) { if (fs.existsSync(`${daemon_dir_1}${daemon_bin}`.replace('${mn_username}', `${mn_username}`))) { mn_process = `${daemon_dir_1}${daemon_bin}`.replace('${mn_username}', `${mn_username}`); } else if (mn_username == 'root') { if (fs.existsSync(`${daemon_dir_root_1}${daemon_bin}`.replace('${mn_username}', `${mn_username}`))) { mn_process = `${daemon_dir_root_1}${daemon_bin}`.replace('${mn_username}', `${mn_username}`); } } } mn_process_cli = mn_process.replace(new RegExp(`${daemon_bin}\$`), '') + daemon_cli; if (args.debug) { console.log(`41 daemon cli: ${mn_process_cli} daemon process: ${mn_process}`); } if (fs.existsSync(mn_process_cli)) { mn_process = `${mn_process_cli} -datadir=/home/${mn_username}/${daemon_data_dir_1}/`; } if (args.debug) { console.log(`42 username: ${mn_username} process: ${mn_process}`); } running_daemon_exec.push(mn_process); users.push(mn_username); }); users = Array.from(new Set(users)); if (args.debug) { console.log('43 running daemons: ' + JSON.stringify(running_daemon_exec, null, 1) + ' users: ' + JSON.stringify(users, null, 1)); } // Count users running. var user_count = 0; var total_users = users.length; for (var i = 0; i < total_users; i++) { if (users[i]) { user_count += 1; } } // Remove dead users from global. var difference = global.bad_status.filter(x => !users.includes(x)); if (args.debug) { // Output debug info. console.log('44 different users' + JSON.stringify(difference, null, 1)); } if (difference.length != 0) { var total_users = difference.length; for (var i = 0; i < total_users; i++) { var diff = difference[i]; delete global.bad_status[diff]; } } // Ping if usercount changed. if (global.usercount != user_count || args.testfail) { var mn_count = users.length; if (!users[0]) { mn_count = 0; } send_chat('warn', `User change detected; Monitoring these users: ${users} \n\nMN Count: ${mn_count}`, 'WARNING'); global.usercount = user_count; } var process_count = 0; users.forEach( function (usrname) { if (usrname === undefined || !usrname) { usrname = 'USER_NONE'; } var mn_process = running_daemon_exec[process_count]; process_count += 1; exec(`${mn_process} getstakingstatus`, (err, stdout_stakestatus, stderr) => { var stake_status = []; if (stdout_stakestatus) { try { stake_status = JSON.parse(stdout_stakestatus); } catch(e) { } } if (stake_status["staking status"] === undefined) { stake_status["staking status"] = 0; } if (args.debug) { console.log('45 getstakingstatus' + JSON.stringify(stake_status, null, 1)); } // Get running masternode status. exec(`${mn_process} masternode status`, (err, stdout_status, stderr) => { if (args.debug) { console.log(`46 username: ${usrname} daemon process: ${mn_process} masternode status: ${stdout_status} error: ${stderr}`); } var mn_status = [] if (stdout_status) { try { mn_status = JSON.parse(stdout_status); } catch(e) { } } if (mn_status.status === undefined) { mn_status.status = -503; } if (!mn_status.netaddr && mn_status.service) { mn_status.netaddr = mn_status.service; } if (stderr.includes("Hot node, waiting for remote activation")) { mn_status.status = 2; mn_status.netaddr = ''; mn_status.notCapableReason = "Hot node, waiting for remote activation"; } if (args.debug) { console.log('47 masternode status ' + JSON.stringify(mn_status, null, 1)); } var time_in_millis = Date.now(); var time_in_seconds = time_in_millis / 1000; // Check masternode status; skip if used for staking. if ((mn_status.status != 4 && mn_status.status != 9) || mn_status.netaddr.endsWith(':0') || args.testfail) { if (!stake_status["staking status"]) { if (args.v || args.debug) { console.log(`51 masternode status number: ${mn_status.status}`); } var display_msg = 0; if (!global.bad_status.hasOwnProperty(usrname)) { global.bad_status[usrname] = []; display_msg = 1; } else if (global.bad_status[usrname][0] + 2700 < time_in_seconds) { display_msg = 2; } else if (global.bad_status[usrname][1] != mn_status.status) { display_msg = 3; } // Ping that the mn is down every 45 minutes. if (display_msg !== 0) { if (args.debug) { console.log('52 bad status ' + JSON.stringify(global.bad_status, null, 1)); } global.bad_status[usrname][0] = time_in_seconds; global.bad_status[usrname][1] = mn_status.status; send_chat('err', `${usrname} is offline! \n${mn_status.netaddr} \nStatus: ${mn_status.status} \nMessage: ${mn_status.notCapableReason}`, 'ERROR'); } } } else if (global.bad_status.hasOwnProperty(usrname) || args.testsuccess || (global.bad_status['USER_NONE'] && user_count != 0)) { if (args.v || args.debug) { console.log(`53 masternode status number: ${mn_status.status}`); } send_chat('success', `${usrname} masternode is online! :wrench: \n${mn_status.netaddr} \nStatus: ${mn_status.status}`, 'GOOD'); delete global.bad_status[usrname]; } // Check block count. exec(`${mn_process} getblockcount`, (err, stdout_block_count, stderr) => { global.stdout_block_count[usrname] = stdout_block_count.trim(); var stdout_block_count = global.stdout_block_count[usrname]; if (args.debug) { console.log(`61 blockcount: ${global.stdout_block_count}`); } if (args.testfail || global.blockcount.hasOwnProperty(usrname)) { // Report blockcount is behind if it hasn't changed in 20 minutes. if (args.testfail || (global.blockcount[usrname][0] + 1200 < time_in_seconds && stdout_block_count == global.blockcount[usrname][2])) { if (args.v || args.debug) { console.log('62 Blockcount Behind.'); } if (args.testfail || global.blockcount[usrname][1] + 1200 < time_in_seconds) { // Send message every 20 minutes. send_chat('warn', `${usrname} blockcount is behind at ${stdout_block_count}!`, 'WARNING'); if (!args.testfail) { global.blockcount[usrname][1] = time_in_seconds; } } } else if (stdout_block_count != global.blockcount[usrname][1]) { update_blockcounter(usrname,time_in_seconds, stdout_block_count) } } else { update_blockcounter(usrname,time_in_seconds, stdout_block_count) } }); // Check connection count. exec(`${mn_process} getconnectioncount`, (err, stdout_connection_count, stderr) => { stdout_connection_count = stdout_connection_count.trim(); if (args.debug) { console.log(`71 Counnection Count: ${stdout_connection_count}`); } if (stdout_connection_count < 3 || args.testfail) { if (args.v || args.debug) { console.log('72 Low Connection Count'); } var display_msg = 0; // Connection count is low after 10 minutes. if (global.connection_count.hasOwnProperty(usrname)) { if (global.connection_count[usrname] + 600 < time_in_seconds) { display_msg = 1; } } else { display_msg = 1; } if (display_msg === 1) { global.connection_count[usrname] = time_in_seconds; send_chat('warn', `${usrname} connection count is low! ${stdout_connection_count}`, 'WARNING'); } } else if (global.connection_count.hasOwnProperty(usrname) || args.testsuccess) { if (args.v || args.debug) { console.log('73 Connection Count Recovered'); } send_chat('success', `${usrname} connection count is ok! :wrench:`, 'GOOD'); delete global.connection_count[usrname]; } }); // See if we won. exec(`${mn_process} masternode winners`, (err, stdout_mn_winners, stderr) => { if (stdout_mn_winners) { try { stdout_mn_winners = JSON.parse(stdout_mn_winners); } catch(e) { } } if (args.debug) { console.log('81 masternode winners: ' + JSON.stringify(stdout_mn_winners, null, 1)); } var current_winners = 0; var block_height = 0; Object.keys(stdout_mn_winners).forEach(function(key) { if (!stdout_mn_winners[key].winner || stdout_mn_winners[key].winner.address === 'Unknown' || current_winners == 1 || typeof mn_status.addr !== 'string') { return; } if (typeof stdout_mn_winners[key].winner.address !== 'string' && stdout_mn_winners[key].winner['0'].address === mn_status.addr) { block_height = stdout_mn_winners[key].nHeight; if (!global.mn_winners[usrname]) { global.mn_winners[usrname] = []; } if (!global.mn_winners[usrname][block_height]) { current_winners = 1; global.mn_winners[usrname][block_height] = stdout_mn_winners[key].winner['0'].address; } } else if (stdout_mn_winners[key].winner.address === mn_status.addr) { block_height = stdout_mn_winners[key].nHeight; if (!global.mn_winners[usrname]) { global.mn_winners[usrname] = []; } if (!global.mn_winners[usrname][block_height]) { current_winners = 1; global.mn_winners[usrname][block_height] = stdout_mn_winners[key].winner.address; } } }); // No winners in the list; clear to not run out of ram. if (block_height == 0) { delete global.mn_winners[usrname]; } if (current_winners) { send_chat('success', `${usrname} is in the masternode winners list! :moneybag: :money_mouth: \nBlocknumber: ${block_height}`, '$$$'); } }); }); }); }); if (user_count != 0 && global.bad_status['USER_NONE']) { delete global.bad_status['USER_NONE']; } }); }; function update_blockcounter(usrname,time_in_seconds, stdout_block_count) { global.blockcount[usrname] = []; global.blockcount[usrname][0] = time_in_seconds; global.blockcount[usrname][1] = 0; global.blockcount[usrname][2] = stdout_block_count; } function info_on_masternodes() { // Get a list of executable files named the same as the daemon in user dirs. exec(`find /home -xdev -executable -name ${daemon_bin} `, (err, stdout_daemon_exec, stderr) => { stdout_daemon_exec = stdout_daemon_exec.trim().split(/\r?\n/).sort(); if (args.debug) { console.log('101 location of daemon binary files:' + JSON.stringify(stdout_daemon_exec, null, 1)); } // Get a list of daemons running. exec(`ps axfo etimes,user:80,command | grep "${daemon_grep}" | grep -v "bash" | awk \'$1 > 30\' | awk \'{ print $2 " " $3}\'`, (err, stdout_users, stderr) => { stdout_users = stdout_users.trim().split(/\r?\n/).sort(); if (args.debug) { console.log(`102 daemon running: ${stdout_users}`); } var time_in_millis = Date.now(); var time_in_seconds = time_in_millis / 1000; // Allow for custom info ping times. var infotimer = 7200; if (args.infotimer) { var infotimer = args.infotimer; } var users = []; var running_daemon_exec = []; stdout_users.forEach( function (user_exec) { var temp = user_exec.trim().split(' '); var mn_process = temp.pop(); var mn_username = temp.pop(); if (!fs.existsSync(mn_process)) { if (fs.existsSync(`${daemon_dir_1}${daemon_bin}`.replace('${mn_username}', `${mn_username}`))) { mn_process = `${daemon_dir_1}${daemon_bin}`.replace('${mn_username}', `${mn_username}`); } } running_daemon_exec.push(mn_process); users.push(mn_username); }); users = Array.from(new Set(users)); if (args.debug) { console.log('103 users: ' + JSON.stringify(users, null, 1) + 'running daemons: ' + JSON.stringify(running_daemon_exec, null, 1)); } // Ping to let user know that this monitor is up. if (users.length) { var users_string = users.toString(); var send_message = 0; if (!global.users_tracked[0]) { send_message = 1; } else if (global.users_tracked[1] != users_string) { send_message = 1; } else if (global.users_tracked[0] + infotimer < time_in_seconds) { send_message = 1; } if (send_message) { send_chat('info', `Monitoring these users: ${users}`, ''); global.users_tracked[0] = time_in_seconds; global.users_tracked[1] = users_string; } } // Check for daemon executables that are not running. var difference = stdout_daemon_exec.filter(x => !running_daemon_exec.includes(x)); if (args.debug) { console.log('104 difference for executables that are not running: ' + JSON.stringify(difference, null, 1)); } if (difference.length != 0 || args.testfail) { var exec_string = difference.toString(); var send_message = 0; if (!global.exec_tracked[0]) { send_message = 1; } else if (global.exec_tracked[1] != exec_string) { send_message = 1; } else if (global.exec_tracked[0] + infotimer < time_in_seconds) { send_message = 1; } if (send_message) { send_chat('err', `These masternodes are offline!\n ${difference}`, 'ERROR'); global.exec_tracked[0] = time_in_seconds; global.exec_tracked[1] = exec_string; } } }); }); }; function send_chat(type, message, user_suffix) { var public_ip = global.public_ip; var monitor_name = mon_name; monitor_name += ` ${user_suffix} `; message += `\n\n${public_ip}` // Create webhook. if (URL_ALERT && (type == 'err' || type == 'success' || type == 'warn')) { try { var Hook = new Webhook(URL_ALERT); } catch (e) { e = JSON.stringify(e); console.log(`201 ${e}`); } } else { try { var Hook = new Webhook(URL); } catch (e) { e = JSON.stringify(e); console.log(`202 ${e}`); } } if (args.v || args.debug) { console.log(`203 ${message}`); } // Send chat. var ret; try { if (!args.local) { ret = Hook[type](monitor_name, message); } } catch (e) { e = JSON.stringify(e); console.log(`204 ${e}`); } return ret; } CATO_MNMON ) >> ~/discord_webhook/cato_mnmon.js cd ~/ rm -rf /home/$USRNAME/discord_webhook/ mv ~/discord_webhook/ /home/$USRNAME/discord_webhook/ sudo chown -R $USRNAME:$USRNAME /home/$USRNAME/discord_webhook/ # Find nodejs EXEC=`sudo find / -xdev -executable -name node | grep bin | head -1` # Setup systemd to start masternode on restart. printf "[Unit] Description=CatoCoin Masternode Monitor running as ${USRNAME} After=network.target [Service] Type=simple User=${USRNAME} WorkingDirectory=/home/${USRNAME} ExecStart=${EXEC} /home/${USRNAME}/discord_webhook/cato_mnmon.js --daemon ExecStop=${EXEC} /home/${USRNAME}/discord_webhook/cato_mnmon.js stop Restart=always RestartSec=10s TimeoutSec=10s OOMScoreAdjust=999 [Install] WantedBy=multi-user.target" > /etc/systemd/system/$USRNAME.service # Run cato master node monitor. systemctl daemon-reload systemctl enable $USRNAME sleep 1 systemctl start $USRNAME systemctl status --no-pager --full $USRNAME echo echo "Commands to control the CatoCoin masternode monitor." echo "systemctl stop ${USRNAME}" echo "systemctl start ${USRNAME}" echo "systemctl status --no-pager --full ${USRNAME}" echo echo "Check your discord server; a message should have been sent." echo