commit 45421e5dfafc1b46e92cf040d8104fb3b485397c Author: Erreur32 Date: Tue Jul 18 23:06:11 2017 +0200 premiere validation diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..f46bef3 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,43 @@ +2017-07-17 + motdstat 0.0.4 become MOTDs32 0.0.5 + - NEW features: add info + - IP + - Hostname with figlet (auto install) + - Show who is connected with SSH + - ADD show numbers of process + +2014-07-17 + motdstat 0.0.4 + - FEATURE: autodiscovery of "netservice" and "process" configuration files + from running network services and processes. (thanks to + Bongermino Björ) + - FEATURE: checking if the "mail" command is installed + - FIXED: hostname detection. Instead of using 'hostname -f' the $HOSTNAME variable is used + - FIXED: '\n' in syslog messages + +2013-02-13 + motdstat 0.0.3 + - FIXED: gaps/spaces in ntp status + - FIXED: swapped yellow/red colors in "Disk Status" column header + - FEATURE: e-mail notification on system reboot + - FEATURE: custom executable commands for Load, Memory, NTP and SWAP tresholds + - FEATURE: custom configurable partition usage (auto-create from /etc/fstab) + - FEATURE: customizable sending messages to local syslog + +2012-10-20 + motdstat 0.0.2 + - fixed: checking if /etc/motd file exists + - fixed: displaying correct swap usage value in instead of swap total value in kb + - fixed: a couple of typo fixes + - fixed: deleting temporary files .motdstat_mail_$$ in temporary directory /tmp + - fixed: handling stderr message(s) when checking ntp status using ntpq command + - fixed: e-mail notification sending when ntp synchronization was lost + - fixed: installation makefile overwrite already installed config files + - changed "swap:" instead of "swaptotal:" in memory column + - changed "memory:" instead of "memtotal:" in memory column + - displaying real memory usage instead of total memory + - updated readme documentation file + +2012-08-14 + motdstat 0.0.1 + - initial release diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..e1d0a6f --- /dev/null +++ b/INSTALL @@ -0,0 +1,67 @@ +MOTDs32 installation instructions +================================== + +Original project: +http://www.gelogic.net/ + +(2017) New Custom project MOTDs32 +https://motds32.echosystem.fr + + +,--. ,--. ,-----. ,--------.,------. ,----. ,---. +| `.' |' .-. ''--. .--'| .-. \ ,---. '.-. |'.-. \ +| |'.'| || | | | | | | | \ :( .-' .' < .-' .' +| | | |' '-' ' | | | '--' /.-' `)/'-' |/ '-. +`--' `--' `-----' `--' `-------' `----' `----' '-----' + + | + Stats32 | + + + +System requirements +=================== + o Unix/Linux operating system with bash + + +Installation instructions +========================= + + As root execute THIS all-in-one command: + + ./Install.sh + + +### OLD way + + 1. try this + + # make install + + 2. Add following lines to /etc/crontab as root (5 minut refresh interval) + + # Message Of The Day - System Status + */5 * * * * root /usr/bin/motds32 --generate + + 3. (Optional) Update the configuration files in /etc/motdstat directory + + +Upgrade instructions +==================== + + 1. As root execute command: + + # make install + + 2. (Optional) Update the configuration files in /etc/motds32 directory + + +Uninstall instructions +====================== + + 1. As root execute command: + # make uninstall + + 2. As root remove the following lines from /etc/crontab + + # Message Of The Day - System Status + */5 * * * * root /usr/bin/motds32 --generate diff --git a/Install.sh b/Install.sh new file mode 100755 index 0000000..1bc84f3 --- /dev/null +++ b/Install.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# +# Installation of MOTDs32 +# Author: Erreur32 +# Version 0.0.5 +# +# MOTDstat is dynamicaly refreshing the /etc/motd file with current informations +# about system status and usage. +# +# Copyright 2017 Erreur32 +# original project: Pavol Krigler +# + +echo -n "Do you wish to install the missing package (y/n)? " +old_stty_cfg=$(stty -g) +stty raw -echo +answer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done ) +stty $old_stty_cfg +if echo "$answer" | grep -iq "^y" ;then + echo "Yes let's go" ; apt-get install -y ntp figlet +else + echo "No continue without"; make install + +fi + + +## install missing package +# apt-get install -y ntp figlet + +## start Installation + +#make install +cp Stats32 /etc/motds32/Stats32 + +## Install Crontab + +echo "add in crontab --> */5 * * * * /usr/bin/motds32 -g (generation each 5 minutes)" +#crontab << FIN +#$(crontab -l) + +### MOTDs32 generation +#*/5 * * * * /etc/motds32/Stats32 > /etc/motds32/Stats32.txt | /usr/bin/motds32 -g 2>1 +### +#FIN + +/usr/bin/motds32 -g + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..85b7038 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +PROGRAMS=motds32 +CONFIGS=motds32.conf + + +usage: + @echo "This makefile allows you to:" + @echo " o install MOTDstat" + @echo " o unintall MOTDstat" + @echo + @echo Examples: + @echo " make install" + @echo " make uninstall" + + +install: + for prog in $(PROGRAMS); do \ + install -m 0755 bin/$$prog /usr/bin; \ + done + + if test ! -d /etc/motds32; then mkdir -p /etc/motds32; fi + + for config in $(CONFIGS); do \ + if test ! -e /etc/motds32/$$config; then install -m 644 ./etc/motds32/$$config /etc/motds32; fi \ + done + + /usr/bin/motds32 -g +uninstall: + for prog in $(PROGRAMS); do \ + rm -f /usr/bin/$$prog; \ + done + rm -rf /etc/motds32/ /etc/motd.full + mv -f /etc/motd.orig /etc/motd diff --git a/README b/README new file mode 100644 index 0000000..1ac947d --- /dev/null +++ b/README @@ -0,0 +1,79 @@ + MOTDstat Documentation +================================================================================ + + MOTDstat will dynamicaly generate the /etc/motd file with current +information about system resources and usage. Using crontab the script will +periodically display status of system resources and services. The original +message of the day is now stored in /etc/motd.orig file. + By configuring e-mail address you'll receive the notifications about +following issues: + + o high CPU load (configurable warning/critical limits) + o exceeding disk space (configurable warning/critical limits on + partitions) + o intensive usage of SWAP space (configurable warning/critical limits) + o specific process is not runnig + o specific local network service is not running + o NTP is not synchronized with network peer(s) (ntpq is required) + o too many e-mails in mail queue (mailq is required) + o system reboot + + + The system status report is organized into three columns: + + o 1st column contains information about partition(s) free space and + usage in percentage. + o 2nd column will display information about used memory and SWAP space + o 3rd column will monitor running processes and local network services + + You are able to modify the disk space warning and critical limits per +mounted filesystem. First time you run the MOTDstat, the fstab_limits file will +be generated with default warning and critical limit. You can setup the limits +by your needs. If some partition is not mounted, the notification e-mail will +be send. + + +INSTALLATION: +============ + type : ./Install.sh + +Customisation: +============= + When you want MOTD to be displayed while logging via SSH client you will +have to change setting on the SSH daemon as following: /etc/ssh/sshd_config + + ~snip~ + PrintMotd yes + ~snip~ + + To apply the changes, restart the SSH server daemon. + + +Sample MOTDstat report +====================== + + hosting.gelogic.net*+ > status at 19:15 > 5min load is 1.06 on 4 cpu(s) + + Disk status | Memory status | Service status +partition free usg | Memory used kB [%] | services (count) +/ 2.7G 41% | Memory: 1013676 97% | tcp/192.168.7.13:80 +/boot 100M 14% | Swap: 6 0% | tcp/192.168.7.13:443 +/var 33G 11% | Buffers: 506268 | tcp/127.0.0.1:3306 +/var/log 8.7G 4% | Cached: 2490812 | tcp/192.168.10.8:22 +/tmp 4.4G 4% | | tcp/127.0.0.1:25 + | | udp/127.0.0.1:161 + | | udp/192.168.10.8:123 + | | ntpd + | | httpd (15) + | | mysqld (2) + | | master + +Explanation of the report +------------------------- + + system hostname report time system load + | | | + | NTP status | | Number of CPU(s) + | | | | | + V V V V V +hosting.gelogic.net*+ > status at 19:15 > 5min load is 1.06 on 4 cpu(s) diff --git a/Stats32 b/Stats32 new file mode 100755 index 0000000..7dda335 --- /dev/null +++ b/Stats32 @@ -0,0 +1,33 @@ +figlet $(hostname) +echo "$SHELL ==> $USER" +echo -e '\e[1;30m'; w +echo -e "\033[0m" + +[ -r /etc/lsb-release ] && . /etc/lsb-release + +if [ -z "$DISTRIB_DESCRIPTION" ] && [ -x /usr/bin/lsb_release ]; then + # Fall back to using the very slow lsb_release utility + DISTRIB_DESCRIPTION=$(lsb_release -s -d) +fi + +printf "\033[5m%s (%s).\n" "$DISTRIB_DESCRIPTION" "$(uname -r)" +echo -e "\033[5mKernel Info: " `uname -smr`; +### bash version + user +#echo -e "\033[5m $SHELL ${BASH_VERSION%.*} $BASH_VERSION \n `ps -p $$` " "\e[0m" +echo -e "\e[0m" +### stats +load=`cat /proc/loadavg | awk '{print $1}'` +root_usage=`df -h / | awk '/\// {print $(NF-1)}'` +memory_usage=`free -m | awk '/Mem:/ { total=$2 } /buffers\/cache/ { used=$3 } END { printf("%3.1f%%", used/total*100)}'` +swap_usage=`free -m | awk '/Swap/ { printf("%3.1f%%", "exit !$2;$3/$2*100") }'` +users=`users | wc -w` +time=`uptime | grep -ohe 'up .*' | sed 's/,/\ hours/g' | awk '{ printf $2" "$3 }'` +processes=`ps aux | wc -l` +ip=`ifconfig $(route | grep default | awk '{ print $8 }') | awk /'inet adr/ {print $2}' | awk -F: '{print $2}'` +printf "%s-----------\t---\t------------\t----------\n" +printf "System load :\t%s\tIP Address:\t%s\n" $load $ip +printf "Memory usage:\t%s\tSystem uptime:\t%s\n" $memory_usage "$time" +printf "Usage on / :\t%s\tSwap usage:\t%s\n" $root_usage $swap_usage +printf "Local Users :\t%s\tProcesses:\t%s\n" $users $processes +printf "%s-----------\t---\t------------\t----------\n" +echo -ne "\n \e[93m "; date ; echo -e "\e[0m" diff --git a/bin/motds32 b/bin/motds32 new file mode 100755 index 0000000..ac51475 --- /dev/null +++ b/bin/motds32 @@ -0,0 +1,666 @@ +#!/bin/bash +# +# Author: Krigler Pavol +# Version 0.0.4 +# +# MOTDs32 is dynamicaly refreshing the /etc/motd file with current informations +# about system status and usage. +# +# Copyright 2013 Pavol Krigler +# +# This file is part of MOTDs32. +# +# MOTDs32 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. +# +# MOTDs32 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 MOTDs32. If not, see . + +/etc/motds32/Stats32 > /etc/motds32/Stats32.txt +# Path to the configuration file +CONFIGFILE="/etc/motds32/motds32.conf" + +# Message of the day file +MOTD='/etc/motd' +MOTD32="/etc/motds32/Stats32" +PRG32="/etc/motds32" +# List of network services which MUST be running +NETSERVICE_LIST="/etc/motds32/netservice" + +# List of processes which MUST be running +PROCESS_LIST="/etc/motds32/process" + +# List of partitions and usage limits +PARTITION_TABLE="/etc/motds32/fstab_limits" + +# List of processes which could be autodiscovered during installation +AUTO_PROCESS_DISCOVERY="sshd apache dhcpd named ntpd nscd postfix slapd smbd atd crond smartd dovecot master mysqld" + +# List of network ports which could be autodiscovered during installation +AUTO_NETSERVICE_DISCOVERY="21 22 25 80 443 123" + +# +# Do not modify lines below +# +VERSION="0.0.5" +LINES=0 + +TMPDIR="/tmp" +STATS_DIR="/var/cache/motds32" +COLL_MEM="$TMPDIR/.motds32_mem_$$" +COLL_DISK="$TMPDIR/.motds32_disk_$$" +COLL_PROC="$TMPDIR/.motds32_proc_$$" +COLL_EMPTY="$TMPDIR/.motds32_empty_$$" + +# Set default VARIABLES +function set_default_values() { + DISK_USAGE_WARNING=80 + DISK_USAGE_CRITICAL=90 + SWAP_USAGE_WARNING=10 + SWAP_USAGE_CRITICAL=30 + CPU_WARNING=90 + CPU_CRITICAL=100 + MAX_ROWS_LIMIT=15 + MAILQ_WARNING=0 + CHECK_NTP="NO" + ENABLE_SYSLOG="NO" + NTP_PROBLEM_EXEC="" + LOAD_TRESHOLD_EXEC="" + SWAP_TRESHOLD_EXEC="" + DISK_TRESHOLD_EXEC="" +} + +# Clean temporary files +test -e $COLL_EMPTY && rm -f $COLL_EMPTY + +# Make a copy of original MOTD file if exits +if [ ! -e "${MOTD}.orig" ]; then + test -e ${MOTD} && cp $MOTD ${MOTD}.orig || touch ${MOTD}.orig +fi + +# +# Generate stats data +# +function stats_data() { +#make stats file in the good directory +$MOTD32 > $PRG32/Stats32.txt +} +#$stats_data +#$MOTD32 > $PRG32/Stats32.txt +# +# Append message to the report (and to syslog if enabled) +# +function report() { + REPORT=$REPORT"${1}\n" + test $ENABLE_SYSLOG = "YES" && logger -t motds32 -- "${1}" +} + +# +# Colored text +# +function red () { + echo -e "\e[1;31m$1\e[0;39m" +} + +function green () { + echo -e "\e[1;32m$1\e[0;39m" +} + +function yellow () { + echo -e "\e[1;33m$1\e[0;39m" +} + +function bold () { + echo -e "\033[1m$1\033[0m" +} + +# Trim the line to 24 characters +function normalize() { + LENGTH=$(echo $PART | wc -c) + + if [ $LENGTH -gt 24 ]; then + CUT=$(echo $PART | sed -e 's/^\(.......\).*\(.............\)/\1...\2/') + PART=$CUT + fi +} + +# +# Generate list of the most used processess +# +function gen_process_list() { + local PROCESS + local PGREP + + echo "Generating \"process\" configuration file to ${PROCESS_LIST} from autodiscovery" + + # Generate process file header + echo "#" > ${PROCESS_LIST} + echo "# List of processes that MUST be running" >> ${PROCESS_LIST} + echo "#" >> ${PROCESS_LIST} + echo "# (The following processes where found through autodiscovery)" >> ${PROCESS_LIST} + + test $(which pgrep 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" && return + + for PROCESS in ${AUTO_PROCESS_DISCOVERY}; do + pgrep -x ${PROCESS} > /dev/null + PGREP=$? + test ${PGREP} -eq 0 && echo ${PROCESS} >> ${PROCESS_LIST} + done +} + +# +# Generate netservice list +# +function gen_netservice_list() { + local NETSTAT + local ENTRY + local RESULT + + echo "Generating \"netservice\" configuration file to ${NETSERVICE_LIST} from autodiscovery" + + # Generate process file header + echo "#" > ${NETSERVICE_LIST} + echo "# List of local network services that MUST be running" >> ${NETSERVICE_LIST} + echo "#" >> ${NETSERVICE_LIST} + echo "# (The following processes where found through autodiscovery)" >> ${NETSERVICE_LIST} + echo "# PROTOCOL IP_ADDRESS:PORT PROCESS NAME" >> ${NETSERVICE_LIST} + + test $(which netstat 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" && return + + NETSTAT=$(netstat -nlp | egrep "^tcp *|^udp *" | sed -e 's/[0-9]*\///' | sed -e 's/LISTEN//' | sed -e 's/: .*$//' | awk '{printf "%-13s%-30s %s\n", $1, $4, $6}') + + for ENTRY in ${AUTO_NETSERVICE_DISCOVERY}; do + RESULT=$(echo "${NETSTAT}" | egrep ":${ENTRY} *") + test -n "${RESULT}" && echo "${RESULT}" >> ${NETSERVICE_LIST} + done +} + +# Process status +function checkprocess { + test ! -f ${PROCESS_LIST} && gen_process_list + + if [ -s ${PROCESS_LIST} ]; then + for PROC in $(egrep -v "^#|^$" ${PROCESS_LIST}); do + PROCES=$(ps ax | grep "$PROC" | grep -v "grep" | grep -vc "checkclient") + if [ $PROCES -gt 0 ]; then + if [ $PROCES -eq 1 ]; then + green "$PROC" >> $COLL_PROC + else + green "$PROC ($PROCES)" | awk '{printf("%-26s %4s\n", $1, $2)}' >> $COLL_PROC + fi + else + report "CRITICAL: Process \"${PROC}\" is not running" + red "$PROC is not running" >> $COLL_PROC + NOT_RUNNING=1 + fi + done + fi + + cp $COLL_PROC ${COLL_PROC}.tmp + + test -n "$NOT_RUNNING" && red " Service status " > $COLL_PROC || green " Service status " > $COLL_PROC + + cat ${COLL_PROC}.tmp >> $COLL_PROC +} + + +# Memory status +function memory() { + bold "Memory used kB [%]" > $COLL_MEM + MEMTOTAL=$(grep "MemTotal" /proc/meminfo | awk '{print $2}') + MEMFREE=$(grep "MemFree" /proc/meminfo | awk '{print $2}') + SWAPTOTAL=$(grep "SwapTotal" /proc/meminfo | awk '{print $2}') + SWAPFREE=$(grep "SwapFree" /proc/meminfo | awk '{print $2}') + + PERCENTOM=$(echo $MEMFREE $MEMTOTAL | awk '{printf("%d", ($2-$1)/$2*100)}') + test $SWAPTOTAL -eq 0 && PERCENTOS=0 || PERCENTOS=$(echo $SWAPFREE $SWAPTOTAL | awk '{printf("%d", ($2-$1)/$2*100)}') + + SWAP=$(printf "Swap: %13d %3d%%\n" $(($SWAPTOTAL-$SWAPFREE)) $PERCENTOS) + + # MEMORY USAGE + printf "Memory: %11d %3d%%\n" $(($MEMTOTAL-$MEMFREE)) $PERCENTOM >> $COLL_MEM + + if [ $PERCENTOS -ge $SWAP_USAGE_CRITICAL ] || [ $SWAPTOTAL -eq 0 ]; then + red "$SWAP" >> $COLL_MEM + CR_MEM=1 + report "WARNING: usage of the SWAP space is more than ${SWAP_USAGE_CRITICAL}%" + + # Execute custom command + if [ -n "${SWAP_TRESHOLD_EXEC}" ]; then + report "WARNING: executing command: \"$SWAP_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${SWAP_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + else + + if [ $PERCENTOS -ge $SWAP_USAGE_WARNING ] && [ $PERCENTOS -lt $SWAP_USAGE_CRITICAL ]; then + yellow "$SWAP" >> $COLL_MEM + WR_MEM=1 + report "CRITICAL: usage of the SWAP space is more than ${SWAP_USAGE_WARNING}%" + # Execute custom command + if [ -n "${SWAP_TRESHOLD_EXEC}" ]; then + report "CRITICAL: executing command: \"$SWAP_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${SWAP_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + else + echo "$SWAP" >> $COLL_MEM + fi + fi + + grep Buffer /proc/meminfo | awk '{printf("%-10s %8s \n",$1,$2)}' >> $COLL_MEM + grep "^Cached" /proc/meminfo | awk '{printf("%-10s %8s \n",$1,$2)}' >> $COLL_MEM + + cp $COLL_MEM ${COLL_MEM}.tmp + + green " Memory status " > $COLL_MEM + test -n "$CR_MEM" && red " Memory status " > $COLL_MEM + test -n "$WR_MEM" && yellow " Memory status " > $COLL_MEM + + cat ${COLL_MEM}.tmp >> $COLL_MEM +} + + +# Disk status +function disk() { + + STATUS_DISK=$(egrep -v "^#|^ *#" /etc/motds32/fstab_limits) + + IFS=$'\n' + + for DEVICE in $STATUS_DISK + do + # Read Mount Point path and limits + MP=$(echo "${DEVICE}" | awk {'print $1'}) + MP_USAGE_WARNING=$(echo "${DEVICE}" | awk {'print $2'}) + MP_USAGE_CRITICAL=$(echo "${DEVICE}" | awk {'print $3'}) + + # If Warning and Critical limit are not set to GLOBAL defaults + test -z ${MP_USAGE_WARNING} && MP_USAGE_WARNING=$DISK_USAGE_WARNING + test -z ${MP_USAGE_CRITICAL} && MP_USAGE_CRITICAL=$DISK_USAGE_CRITICAL + + + # Fixed problems with long device names and new line + PART_RESULT=$(df -h "${MP}" 2>/dev/null) + DF_RESULT=$? + + # Check if the partition exists + if [ ${DF_RESULT} -eq 0 ]; then + + PART=$(echo "${PART_RESULT}" | grep '\/' | tr -d '\n' | awk '{printf("%-13s %5s %4s\n",$6,$4,$5)}' | tr -d '%') + + normalize + + USAGE=$(echo $PART | awk '{print $3}') + PARTITION=$(echo $PART | awk '{print $1}') + + if [ $USAGE -ge $MP_USAGE_WARNING ] && [ $USAGE -lt $MP_USAGE_CRITICAL ]; then + yellow "${PART}%" >> $COLL_DISK && WR_NOTICE=1 + report "WARNING: usage of $PARTITION partition is ${USAGE}% and limit is ${MP_USAGE_WARNING}%" + + # Execute custom command + if [ -n "${DISK_TRESHOLD_EXEC}" ]; then + report "WARNING: executing command: \"$DISK_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${DISK_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + continue + fi + + if [ $USAGE -ge $MP_USAGE_CRITICAL ]; then + red "${PART}%" >> $COLL_DISK && CR_NOTICE=1 + report "CRITICAL: usage of $PARTITION partition is ${USAGE}% and limit is ${MP_USAGE_CRITICAL}%" + + # Execute custom command + if [ -n "${DISK_TRESHOLD_EXEC}" ]; then + report "CRITICAL: executing command: \"$DISK_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${DISK_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + fi + test $USAGE -lt $MP_USAGE_WARNING && echo "${PART}%" >> $COLL_DISK + + + else + red "$(printf "%-13.13s NotMounted" ${MP})" >> $COLL_DISK && CR_NOTICE=1 + report "CRITICAL: mount point $MP is not mounted" + fi + + done + + unset IFS + + cat $COLL_DISK | sort -k 3 -nr > ${COLL_DISK}.tmp + + green " Disk status " > $COLL_DISK + test -n "$CR_NOTICE" && red " Disk status " > $COLL_DISK + test -n "$WR_NOTICE" && yellow " Disk status " > $COLL_DISK + + + bold "partition free usg" >> $COLL_DISK + cat ${COLL_DISK}.tmp >> $COLL_DISK +} + +function getmaxrow () { + DISK_NUM=$(wc -l $COLL_DISK | awk '{print $1}') + test $DISK_NUM -gt $LINES && LINES=$DISK_NUM + MEM_NUM=$(wc -l $COLL_MEM | awk '{print $1}') + test $MEM_NUM -gt $LINES && LINES=$MEM_NUM + PROC_NUM=$(wc -l $COLL_PROC | awk '{print $1}') + test $PROC_NUM -gt $LINES && LINES=$PROC_NUM +} + +# Join 3 cols together +function join () { + getmaxrow + MEM_NUM=$(($MEM_NUM + 1)) + DISK_NUM=$(($DISK_NUM + 1)) + + test -e $COLL_EMPTY && rm -f $COLL_EMPTY + for I in $(seq $DISK_NUM $LINES); do echo " " >> $COLL_DISK; done + for I in $(seq $MEM_NUM $LINES); do echo " " >> $COLL_MEM; done + + for I in $(seq $LINES); do echo ' | ' >> $COLL_EMPTY; done +} + +# Center text +function echo_center { + LENGTH=${#1} + printf "%$(((80 - $LENGTH) / 2))s" ; echo "$1" +} +function cat_center { + LENGTH=${#1} + printf "%$(((80 - $LENGTH) / 2))s" ; cat $1 +} + +# +# Check listen services IP:Port +# +function checkservices { + test ! -f ${NETSERVICE_LIST} && gen_netservice_list + + bold "service(s) (count)" > $COLL_PROC + + NETSTAT=$(netstat -nlp 2>/dev/null) + + if [ -e ${NETSERVICE_LIST} ]; then + IFS=$'\n' + + LINE=$(grep -v "^#" ${NETSERVICE_LIST}) + for SERVICE in $LINE; do + PROTO=$(echo $SERVICE | awk '{print $1}') + SOCKET=$(echo $SERVICE | awk '{print $2}') + PROCES=$(echo $SERVICE | awk '{print $3}') + + RESULT=$(echo "$NETSTAT" | egrep "^${PROTO}.*${SOCKET}") + + # First checking + if [ -z "$RESULT" ]; then + # DEBUG + # echo $NETSTAT >> /tmp/.motds32_netstat_$$.tmp + sleep 1 + + NETSTAT=$(netstat -nlp 2>/dev/null) + # Double checking for fake panic + RESULT=$(echo "$NETSTAT" | egrep "^${PROTO}.*${SOCKET}") + + if [ -z "$RESULT" ]; then + red "${PROTO}/${SOCKET}" >> $COLL_PROC + NOT_RUNNING=1 + report "CRITICAL: Service \"$PROCES\" not running at socket ${PROTO}/${SOCKET}" + else + green "${PROTO}/${SOCKET}" >> $COLL_PROC + fi + else + green "${PROTO}/${SOCKET}" >> $COLL_PROC + fi + done + + unset IFS + fi + +} + +function load { + NUM_OF_CPU=$(grep -i processor /proc/cpuinfo | wc -l) + LOAD_5MIN=$(awk '{printf $2}' < /proc/loadavg) + LOAD_PERCENT=$(echo $LOAD_5MIN $NUM_OF_CPU |awk '{printf "%2.f", $1*100/$2}') + + LOAD_REPORT="5min load is $LOAD_5MIN on $NUM_OF_CPU cpu(s)" + + if [ $LOAD_PERCENT -gt $CPU_WARNING ] && [ $LOAD_PERCENT -lt $CPU_CRITICAL ]; then + report "WARNING: System load ${LOAD_5MIN} on $NUM_OF_CPU cpu(s)" + + # Execute custom command + if [ -n "${LOAD_TRESHOLD_EXEC}" ]; then + report "WARNING: executing command: \"$LOAD_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${LOAD_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + return + fi + if [ $LOAD_PERCENT -gt $CPU_CRITICAL ]; then + report "CRITICAL: system load ${LOAD_5MIN} on $NUM_OF_CPU cpu(s)" + + # Execute custom command + if [ -n "${LOAD_TRESHOLD_EXEC}" ]; then + report "CRITICAL: executing command: \"$LOAD_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${LOAD_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + fi +} + +# +# Notify when too much e-mails in mailq +# if MAILQ_WARNING=0 skip this test +# +function check_mailq { + + test $MAILQ_WARNING -eq 0 && return + + # Check if mail command is present in $PATH + if [ $(which mailq 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" ]; then + echo "WARNING: unable to find mailq command in \$PATH" + return + fi + + MAILQ=$(mailq | tail -n 1 | grep -i requests | awk '{print $5}') + + if [ -n "$MAILQ" ]; then + if [ $MAILQ -gt $MAILQ_WARNING ]; then +# REPORT=$REPORT"WARNING: mail queue size warning $MAILQ > $MAILQ_WARNING\n" + report "WARNING: mail queue size warning $MAILQ > $MAILQ_WARNING" + fi + fi +} + +# Check NTP status +function check_ntp() { + test $CHECK_NTP != "YES" && NTPSTATUS=" " && return + + if [ $(which ntpq 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" ]; then + echo "WARNING: unable to find ntpq command in \$PATH" + return + fi + + NTP=$(ntpq -p 2>/dev/null | egrep -v '===|remote|^ ' | sed -e 's/^\(.\).*/\1/' | tr -d '\n') + if [ $(echo "$NTP" | grep -c '\*') -eq 0 ]; then + test -z "$NTP" && NTPSTATUS=$(red "x") || NTPSTATUS=$(red "$NTP") + report "WARNING: NTP synchronization lost" + if [ -n "${NTP_PROBLEM_EXEC}" ]; then + report "WARNING: executing command: \"$NTP_PROBLEM_EXEC\"" + report "--->%---" + report "$($NTP_PROBLEM_EXEC 2>&1)" + report "--->%---" + fi + else + NTPSTATUS=$(green "$NTP") + fi +} + +# +# Generate mount point table usage limits from /etc/fstab file +# If Warning or Critical mimits are not set user GLOBAL settings +# +function gen_mount_point_table() { + + if [ ! -e ${PARTITION_TABLE} ]; then + + # Exclude list : '#' comments and blacklisted filesystems + FSTAB=$(egrep -v '^#|^ .*#|[[:space:]]proc[[:space:]]|[[:space:]]debugfs[[:space:]]|[[:space:]]swap[[:space:]]|[[:space:]]devpts[[:space:]]|[[:space:]]sysfs[[:space:]]|[[:space:]]tmpfs[[:space:]]' < /etc/fstab | awk '{print($2)}') + # Generate header + echo "# Mount point usage limit Warn [%] Critical [%]" > ${PARTITION_TABLE} + + for PARTITION in $FSTAB; do + printf "%-28s %s %s\n" ${PARTITION} ${DISK_USAGE_WARNING} ${DISK_USAGE_CRITICAL} >> ${PARTITION_TABLE} + done + fi +} + +# display usage +function usage { + echo "Usage: motds32 OPTIONS" + + echo " -g, --generate Check system status and generate it to $MOTD file" + echo " -s, --status Show limited content of MOTD file" + echo " -v, --version Display information about motds32 version and author" + echo " -c, --check Check stats motd file" + exit 0 +} + +# +# Main program +# + + +# Read the configuration file +if [ -e $CONFIGFILE ]; then + set_default_values + . $CONFIGFILE +else + echo "Error: configuration file $CONFIGFILE not found" + exit 1 +fi + +case $1 in + -g|--generate) + ;; + + -s|--status) + test -e $MOTD && cat $MOTD + exit 0 + ;; + + -v|--version) + echo "motds32 version $VERSION" + echo "Author: Krigler Pavol, e-mail: motds32@gelogic.net" + echo "Custom: Erreur32" + exit 0 + ;; + -c|--check) + echo "motds32 check motd file" + cat /etc/motd + exit 0 + ;; + *) + usage + ;; +esac + +# Generate mount point table usage limits from /etc/fstab file +gen_mount_point_table + +# Create e-mail REPORT header +REPORT="System report\n=============\nHost: ${HOSTNAME}\nDate: $(date)\n" + +# Get disk statistics +disk + +# Get memorty statistics +memory + +# Check local network service +checkservices + +# Check local running processes +checkprocess + +# Join all output to multiple columns +join + +# Check the mailq +check_mailq + +# Check NTP status +check_ntp + +# Create MOTD system status header +load + +echo_center "${HOSTNAME}${NTPSTATUS} > status at $(date +"%R") > ${LOAD_REPORT}" bold > $MOTD + +echo >> $MOTD + +# cat /etc/motds32/Stats32.txt >> $MOTD + +paste -d "" $COLL_DISK $COLL_EMPTY $COLL_MEM $COLL_EMPTY $COLL_PROC | head -n $MAX_ROWS_LIMIT >> $MOTD +cat /etc/motds32/Stats32.txt >> $MOTD + +# Full report +paste -d "" $COLL_DISK $COLL_EMPTY $COLL_MEM $COLL_EMPTY $COLL_PROC > $MOTD.full + +echo >> $MOTD + +# Append original MOTD from /etc/motd.orig file if exists +if [ -s ${MOTD}.orig ]; then + echo_center "--- Message Of The Day --- (from /etc/motd.orig file) ---" >> $MOTD + echo >> $MOTD + cat ${MOTD}.orig >> $MOTD +fi + +# Create statistics directory +test ! -e $STATS_DIR && mkdir -p $STATS_DIR +#/etc/motds32/Stats32 > /etc/motds32/Stats32.txt + +# Check if server has rebooted (current uptime < stored uptime +if [ -s $STATS_DIR/uptime ]; then + if [ $(cut -d '.' -f 1 < /proc/uptime) -lt $(cat $STATS_DIR/uptime) ]; then + report "CRITICAL: System has rebooted." + fi +fi + +# Update uptime statistics +test -e /proc/uptime && cut -d '.' -f 1 < /proc/uptime > $STATS_DIR/uptime + + +# Removing temporary files +rm -f ${COLL_PROC} ${COLL_PROC}.tmp +rm -f ${COLL_MEM} ${COLL_MEM}.tmp +rm -f ${COLL_DISK} ${COLL_DISK}.tmp +rm -f ${COLL_EMPTY} + +if [ ! -z "$EMAIL" ]; then + + # Check if mail command is present in $PATH + if [ $(which mail 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" ]; then + echo "WARNING: unable to find mail command in \$PATH" + else + if [ ! -z "$(echo -e "$REPORT" | egrep -i "warning:|critical:")" ]; then + echo -e "$REPORT" | mail "$EMAIL" -s "MOTDs32 alert" + fi + fi +fi diff --git a/bin/motdstat b/bin/motdstat new file mode 100755 index 0000000..808b0ee --- /dev/null +++ b/bin/motdstat @@ -0,0 +1,641 @@ +#!/bin/bash +# +# Author: Krigler Pavol +# Version 0.0.4 +# +# MOTDstat is dynamicaly refreshing the /etc/motd file with current informations +# about system status and usage. +# +# Copyright 2013 Pavol Krigler +# +# This file is part of MOTDstat. +# +# MOTDstat 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. +# +# MOTDstat 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 MOTDstat. If not, see . + + +# Path to the configuration file +CONFIGFILE="/etc/motdstat/motdstat.conf" + +# Message of the day file +MOTD='/etc/motd' + +# List of network services which MUST be running +NETSERVICE_LIST="/etc/motdstat/netservice" + +# List of processes which MUST be running +PROCESS_LIST="/etc/motdstat/process" + +# List of partitions and usage limits +PARTITION_TABLE="/etc/motdstat/fstab_limits" + +# List of processes which could be autodiscovered during installation +AUTO_PROCESS_DISCOVERY="sshd apache dhcpd named ntpd nscd postfix slapd smbd atd crond smartd dovecot master mysqld" + +# List of network ports which could be autodiscovered during installation +AUTO_NETSERVICE_DISCOVERY="21 22 25 80 443 123" + +# +# Do not modify lines below +# +VERSION="0.0.4" +LINES=0 + +TMPDIR="/tmp" +STATS_DIR="/var/cache/motdstat" +COLL_MEM="$TMPDIR/.motdstat_mem_$$" +COLL_DISK="$TMPDIR/.motdstat_disk_$$" +COLL_PROC="$TMPDIR/.motdstat_proc_$$" +COLL_EMPTY="$TMPDIR/.motdstat_empty_$$" + +# Set default VARIABLES +function set_default_values() { + DISK_USAGE_WARNING=80 + DISK_USAGE_CRITICAL=90 + SWAP_USAGE_WARNING=10 + SWAP_USAGE_CRITICAL=30 + CPU_WARNING=90 + CPU_CRITICAL=100 + MAX_ROWS_LIMIT=15 + MAILQ_WARNING=0 + CHECK_NTP="NO" + ENABLE_SYSLOG="NO" + NTP_PROBLEM_EXEC="" + LOAD_TRESHOLD_EXEC="" + SWAP_TRESHOLD_EXEC="" + DISK_TRESHOLD_EXEC="" +} + +# Clean temporary files +test -e $COLL_EMPTY && rm -f $COLL_EMPTY + +# Make a copy of original MOTD file if exits +if [ ! -e "${MOTD}.orig" ]; then + test -e ${MOTD} && cp $MOTD ${MOTD}.orig || touch ${MOTD}.orig +fi + + +# +# Append message to the report (and to syslog if enabled) +# +function report() { + REPORT=$REPORT"${1}\n" + test $ENABLE_SYSLOG = "YES" && logger -t motdstat -- "${1}" +} + +# +# Colored text +# +function red () { + echo -e "\e[1;31m$1\e[0;39m" +} + +function green () { + echo -e "\e[1;32m$1\e[0;39m" +} + +function yellow () { + echo -e "\e[1;33m$1\e[0;39m" +} + +function bold () { + echo -e "\033[1m$1\033[0m" +} + +# Trim the line to 24 characters +function normalize() { + LENGTH=$(echo $PART | wc -c) + + if [ $LENGTH -gt 24 ]; then + CUT=$(echo $PART | sed -e 's/^\(.......\).*\(.............\)/\1...\2/') + PART=$CUT + fi +} + +# +# Generate list of the most used processess +# +function gen_process_list() { + local PROCESS + local PGREP + + echo "Generating \"process\" configuration file to ${PROCESS_LIST} from autodiscovery" + + # Generate process file header + echo "#" > ${PROCESS_LIST} + echo "# List of processes that MUST be running" >> ${PROCESS_LIST} + echo "#" >> ${PROCESS_LIST} + echo "# (The following processes where found through autodiscovery)" >> ${PROCESS_LIST} + + test $(which pgrep 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" && return + + for PROCESS in ${AUTO_PROCESS_DISCOVERY}; do + pgrep -x ${PROCESS} > /dev/null + PGREP=$? + test ${PGREP} -eq 0 && echo ${PROCESS} >> ${PROCESS_LIST} + done +} + +# +# Generate netservice list +# +function gen_netservice_list() { + local NETSTAT + local ENTRY + local RESULT + + echo "Generating \"netservice\" configuration file to ${NETSERVICE_LIST} from autodiscovery" + + # Generate process file header + echo "#" > ${NETSERVICE_LIST} + echo "# List of local network services that MUST be running" >> ${NETSERVICE_LIST} + echo "#" >> ${NETSERVICE_LIST} + echo "# (The following processes where found through autodiscovery)" >> ${NETSERVICE_LIST} + echo "# PROTOCOL IP_ADDRESS:PORT PROCESS NAME" >> ${NETSERVICE_LIST} + + test $(which netstat 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" && return + + NETSTAT=$(netstat -nlp | egrep "^tcp *|^udp *" | sed -e 's/[0-9]*\///' | sed -e 's/LISTEN//' | sed -e 's/: .*$//' | awk '{printf "%-13s%-30s %s\n", $1, $4, $6}') + + for ENTRY in ${AUTO_NETSERVICE_DISCOVERY}; do + RESULT=$(echo "${NETSTAT}" | egrep ":${ENTRY} *") + test -n "${RESULT}" && echo "${RESULT}" >> ${NETSERVICE_LIST} + done +} + +# Process status +function checkprocess { + test ! -f ${PROCESS_LIST} && gen_process_list + + if [ -s ${PROCESS_LIST} ]; then + for PROC in $(egrep -v "^#|^$" ${PROCESS_LIST}); do + PROCES=$(ps ax | grep "$PROC" | grep -v "grep" | grep -vc "checkclient") + if [ $PROCES -gt 0 ]; then + if [ $PROCES -eq 1 ]; then + green "$PROC" >> $COLL_PROC + else + green "$PROC ($PROCES)" | awk '{printf("%-26s %4s\n", $1, $2)}' >> $COLL_PROC + fi + else + report "CRITICAL: Process \"${PROC}\" is not running" + red "$PROC is not running" >> $COLL_PROC + NOT_RUNNING=1 + fi + done + fi + + cp $COLL_PROC ${COLL_PROC}.tmp + + test -n "$NOT_RUNNING" && red " Service status " > $COLL_PROC || green " Service status " > $COLL_PROC + + cat ${COLL_PROC}.tmp >> $COLL_PROC +} + + +# Memory status +function memory() { + bold "Memory used kB [%]" > $COLL_MEM + MEMTOTAL=$(grep "MemTotal" /proc/meminfo | awk '{print $2}') + MEMFREE=$(grep "MemFree" /proc/meminfo | awk '{print $2}') + SWAPTOTAL=$(grep "SwapTotal" /proc/meminfo | awk '{print $2}') + SWAPFREE=$(grep "SwapFree" /proc/meminfo | awk '{print $2}') + + PERCENTOM=$(echo $MEMFREE $MEMTOTAL | awk '{printf("%d", ($2-$1)/$2*100)}') + test $SWAPTOTAL -eq 0 && PERCENTOS=0 || PERCENTOS=$(echo $SWAPFREE $SWAPTOTAL | awk '{printf("%d", ($2-$1)/$2*100)}') + + SWAP=$(printf "Swap: %13d %3d%%\n" $(($SWAPTOTAL-$SWAPFREE)) $PERCENTOS) + + # MEMORY USAGE + printf "Memory: %11d %3d%%\n" $(($MEMTOTAL-$MEMFREE)) $PERCENTOM >> $COLL_MEM + + if [ $PERCENTOS -ge $SWAP_USAGE_CRITICAL ] || [ $SWAPTOTAL -eq 0 ]; then + red "$SWAP" >> $COLL_MEM + CR_MEM=1 + report "WARNING: usage of the SWAP space is more than ${SWAP_USAGE_CRITICAL}%" + + # Execute custom command + if [ -n "${SWAP_TRESHOLD_EXEC}" ]; then + report "WARNING: executing command: \"$SWAP_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${SWAP_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + else + + if [ $PERCENTOS -ge $SWAP_USAGE_WARNING ] && [ $PERCENTOS -lt $SWAP_USAGE_CRITICAL ]; then + yellow "$SWAP" >> $COLL_MEM + WR_MEM=1 + report "CRITICAL: usage of the SWAP space is more than ${SWAP_USAGE_WARNING}%" + # Execute custom command + if [ -n "${SWAP_TRESHOLD_EXEC}" ]; then + report "CRITICAL: executing command: \"$SWAP_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${SWAP_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + else + echo "$SWAP" >> $COLL_MEM + fi + fi + + grep Buffer /proc/meminfo | awk '{printf("%-10s %8s \n",$1,$2)}' >> $COLL_MEM + grep "^Cached" /proc/meminfo | awk '{printf("%-10s %8s \n",$1,$2)}' >> $COLL_MEM + + cp $COLL_MEM ${COLL_MEM}.tmp + + green " Memory status " > $COLL_MEM + test -n "$CR_MEM" && red " Memory status " > $COLL_MEM + test -n "$WR_MEM" && yellow " Memory status " > $COLL_MEM + + cat ${COLL_MEM}.tmp >> $COLL_MEM +} + + +# Disk status +function disk() { + + STATUS_DISK=$(egrep -v "^#|^ *#" /etc/motdstat/fstab_limits) + + IFS=$'\n' + + for DEVICE in $STATUS_DISK + do + # Read Mount Point path and limits + MP=$(echo "${DEVICE}" | awk {'print $1'}) + MP_USAGE_WARNING=$(echo "${DEVICE}" | awk {'print $2'}) + MP_USAGE_CRITICAL=$(echo "${DEVICE}" | awk {'print $3'}) + + # If Warning and Critical limit are not set to GLOBAL defaults + test -z ${MP_USAGE_WARNING} && MP_USAGE_WARNING=$DISK_USAGE_WARNING + test -z ${MP_USAGE_CRITICAL} && MP_USAGE_CRITICAL=$DISK_USAGE_CRITICAL + + + # Fixed problems with long device names and new line + PART_RESULT=$(df -h "${MP}" 2>/dev/null) + DF_RESULT=$? + + # Check if the partition exists + if [ ${DF_RESULT} -eq 0 ]; then + + PART=$(echo "${PART_RESULT}" | grep '\/' | tr -d '\n' | awk '{printf("%-13s %5s %4s\n",$6,$4,$5)}' | tr -d '%') + + normalize + + USAGE=$(echo $PART | awk '{print $3}') + PARTITION=$(echo $PART | awk '{print $1}') + + if [ $USAGE -ge $MP_USAGE_WARNING ] && [ $USAGE -lt $MP_USAGE_CRITICAL ]; then + yellow "${PART}%" >> $COLL_DISK && WR_NOTICE=1 + report "WARNING: usage of $PARTITION partition is ${USAGE}% and limit is ${MP_USAGE_WARNING}%" + + # Execute custom command + if [ -n "${DISK_TRESHOLD_EXEC}" ]; then + report "WARNING: executing command: \"$DISK_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${DISK_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + continue + fi + + if [ $USAGE -ge $MP_USAGE_CRITICAL ]; then + red "${PART}%" >> $COLL_DISK && CR_NOTICE=1 + report "CRITICAL: usage of $PARTITION partition is ${USAGE}% and limit is ${MP_USAGE_CRITICAL}%" + + # Execute custom command + if [ -n "${DISK_TRESHOLD_EXEC}" ]; then + report "CRITICAL: executing command: \"$DISK_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${DISK_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + fi + test $USAGE -lt $MP_USAGE_WARNING && echo "${PART}%" >> $COLL_DISK + + + else + red "$(printf "%-13.13s NotMounted" ${MP})" >> $COLL_DISK && CR_NOTICE=1 + report "CRITICAL: mount point $MP is not mounted" + fi + + done + + unset IFS + + cat $COLL_DISK | sort -k 3 -nr > ${COLL_DISK}.tmp + + green " Disk status " > $COLL_DISK + test -n "$CR_NOTICE" && red " Disk status " > $COLL_DISK + test -n "$WR_NOTICE" && yellow " Disk status " > $COLL_DISK + + + bold "partition free usg" >> $COLL_DISK + cat ${COLL_DISK}.tmp >> $COLL_DISK +} + +function getmaxrow () { + DISK_NUM=$(wc -l $COLL_DISK | awk '{print $1}') + test $DISK_NUM -gt $LINES && LINES=$DISK_NUM + MEM_NUM=$(wc -l $COLL_MEM | awk '{print $1}') + test $MEM_NUM -gt $LINES && LINES=$MEM_NUM + PROC_NUM=$(wc -l $COLL_PROC | awk '{print $1}') + test $PROC_NUM -gt $LINES && LINES=$PROC_NUM +} + +# Join 3 cols together +function join () { + getmaxrow + MEM_NUM=$(($MEM_NUM + 1)) + DISK_NUM=$(($DISK_NUM + 1)) + + test -e $COLL_EMPTY && rm -f $COLL_EMPTY + for I in $(seq $DISK_NUM $LINES); do echo " " >> $COLL_DISK; done + for I in $(seq $MEM_NUM $LINES); do echo " " >> $COLL_MEM; done + + for I in $(seq $LINES); do echo ' | ' >> $COLL_EMPTY; done +} + +# Center text +function echo_center { + LENGTH=${#1} + printf "%$(((80 - $LENGTH) / 2))s" ; echo "$1" +} + +# +# Check listen services IP:Port +# +function checkservices { + test ! -f ${NETSERVICE_LIST} && gen_netservice_list + + bold "service(s) (count)" > $COLL_PROC + + NETSTAT=$(netstat -nlp 2>/dev/null) + + if [ -e ${NETSERVICE_LIST} ]; then + IFS=$'\n' + + LINE=$(grep -v "^#" ${NETSERVICE_LIST}) + for SERVICE in $LINE; do + PROTO=$(echo $SERVICE | awk '{print $1}') + SOCKET=$(echo $SERVICE | awk '{print $2}') + PROCES=$(echo $SERVICE | awk '{print $3}') + + RESULT=$(echo "$NETSTAT" | egrep "^${PROTO}.*${SOCKET}") + + # First checking + if [ -z "$RESULT" ]; then + # DEBUG + # echo $NETSTAT >> /tmp/.motdstat_netstat_$$.tmp + sleep 1 + + NETSTAT=$(netstat -nlp 2>/dev/null) + # Double checking for fake panic + RESULT=$(echo "$NETSTAT" | egrep "^${PROTO}.*${SOCKET}") + + if [ -z "$RESULT" ]; then + red "${PROTO}/${SOCKET}" >> $COLL_PROC + NOT_RUNNING=1 + report "CRITICAL: Service \"$PROCES\" not running at socket ${PROTO}/${SOCKET}" + else + green "${PROTO}/${SOCKET}" >> $COLL_PROC + fi + else + green "${PROTO}/${SOCKET}" >> $COLL_PROC + fi + done + + unset IFS + fi + +} + +function load { + NUM_OF_CPU=$(grep -i processor /proc/cpuinfo | wc -l) + LOAD_5MIN=$(awk '{printf $2}' < /proc/loadavg) + LOAD_PERCENT=$(echo $LOAD_5MIN $NUM_OF_CPU |awk '{printf "%2.f", $1*100/$2}') + + LOAD_REPORT="5min load is $LOAD_5MIN on $NUM_OF_CPU cpu(s)" + + if [ $LOAD_PERCENT -gt $CPU_WARNING ] && [ $LOAD_PERCENT -lt $CPU_CRITICAL ]; then + report "WARNING: System load ${LOAD_5MIN} on $NUM_OF_CPU cpu(s)" + + # Execute custom command + if [ -n "${LOAD_TRESHOLD_EXEC}" ]; then + report "WARNING: executing command: \"$LOAD_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${LOAD_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + return + fi + if [ $LOAD_PERCENT -gt $CPU_CRITICAL ]; then + report "CRITICAL: system load ${LOAD_5MIN} on $NUM_OF_CPU cpu(s)" + + # Execute custom command + if [ -n "${LOAD_TRESHOLD_EXEC}" ]; then + report "CRITICAL: executing command: \"$LOAD_TRESHOLD_EXEC\"" + report "--->%---" + report "$(eval ${LOAD_TRESHOLD_EXEC} 2>&1)" + report "--->%---" + fi + fi +} + +# +# Notify when too much e-mails in mailq +# if MAILQ_WARNING=0 skip this test +# +function check_mailq { + + test $MAILQ_WARNING -eq 0 && return + + # Check if mail command is present in $PATH + if [ $(which mailq 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" ]; then + echo "WARNING: unable to find mailq command in \$PATH" + return + fi + + MAILQ=$(mailq | tail -n 1 | grep -i requests | awk '{print $5}') + + if [ -n "$MAILQ" ]; then + if [ $MAILQ -gt $MAILQ_WARNING ]; then +# REPORT=$REPORT"WARNING: mail queue size warning $MAILQ > $MAILQ_WARNING\n" + report "WARNING: mail queue size warning $MAILQ > $MAILQ_WARNING" + fi + fi +} + +# Check NTP status +function check_ntp() { + test $CHECK_NTP != "YES" && NTPSTATUS=" " && return + + if [ $(which ntpq 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" ]; then + echo "WARNING: unable to find ntpq command in \$PATH" + return + fi + + NTP=$(ntpq -p 2>/dev/null | egrep -v '===|remote|^ ' | sed -e 's/^\(.\).*/\1/' | tr -d '\n') + if [ $(echo "$NTP" | grep -c '\*') -eq 0 ]; then + test -z "$NTP" && NTPSTATUS=$(red "x") || NTPSTATUS=$(red "$NTP") + report "WARNING: NTP synchronization lost" + if [ -n "${NTP_PROBLEM_EXEC}" ]; then + report "WARNING: executing command: \"$NTP_PROBLEM_EXEC\"" + report "--->%---" + report "$($NTP_PROBLEM_EXEC 2>&1)" + report "--->%---" + fi + else + NTPSTATUS=$(green "$NTP") + fi +} + +# +# Generate mount point table usage limits from /etc/fstab file +# If Warning or Critical mimits are not set user GLOBAL settings +# +function gen_mount_point_table() { + + if [ ! -e ${PARTITION_TABLE} ]; then + + # Exclude list : '#' comments and blacklisted filesystems + FSTAB=$(egrep -v '^#|^ .*#|[[:space:]]proc[[:space:]]|[[:space:]]debugfs[[:space:]]|[[:space:]]swap[[:space:]]|[[:space:]]devpts[[:space:]]|[[:space:]]sysfs[[:space:]]|[[:space:]]tmpfs[[:space:]]' < /etc/fstab | awk '{print($2)}') + # Generate header + echo "# Mount point usage limit Warn [%] Critical [%]" > ${PARTITION_TABLE} + + for PARTITION in $FSTAB; do + printf "%-28s %s %s\n" ${PARTITION} ${DISK_USAGE_WARNING} ${DISK_USAGE_CRITICAL} >> ${PARTITION_TABLE} + done + fi +} + +# display usage +function usage { + echo "Usage: motdstat OPTIONS" + + echo " -g, --generate Check system status and generate it to $MOTD file" + echo " -s, --status Show limited content of MOTD file" + echo " -v, --version Display information about motdstat version and author" + exit 0 +} + +# +# Main program +# + + +# Read the configuration file +if [ -e $CONFIGFILE ]; then + set_default_values + . $CONFIGFILE +else + echo "Error: configuration file $CONFIGFILE not found" + exit 1 +fi + +case $1 in + -g|--generate) + ;; + + -s|--status) + test -e $MOTD && cat $MOTD + exit 0 + ;; + + -v|--version) + echo "motdstat version $VERSION" + echo "Author: Krigler Pavol, e-mail: motdstat@gelogic.net" + exit 0 + ;; + *) + usage + ;; +esac + +# Generate mount point table usage limits from /etc/fstab file +gen_mount_point_table + +# Create e-mail REPORT header +REPORT="System report\n=============\nHost: ${HOSTNAME}\nDate: $(date)\n" + +# Get disk statistics +disk + +# Get memorty statistics +memory + +# Check local network service +checkservices + +# Check local running processes +checkprocess + +# Join all output to multiple columns +join + +# Check the mailq +check_mailq + +# Check NTP status +check_ntp + +# Create MOTD system status header +load +echo_center "${HOSTNAME}${NTPSTATUS} > status at $(date +"%R") > ${LOAD_REPORT}" bold > $MOTD + +echo >> $MOTD + +paste -d "" $COLL_DISK $COLL_EMPTY $COLL_MEM $COLL_EMPTY $COLL_PROC | head -n $MAX_ROWS_LIMIT | cat /etc/motds32/Stats32.txt >> $MOTD + +# Full report +paste -d "" $COLL_DISK $COLL_EMPTY $COLL_MEM $COLL_EMPTY $COLL_PROC | cat /etc/motds32/Stats32.txt > $MOTD.full + +echo >> $MOTD + +# Append original MOTD from /etc/motd.orig file if exists +if [ -s ${MOTD}.orig ]; then + echo_center "--- Message Of The Day --- (from /etc/motd.orig file) ---" >> $MOTD + echo >> $MOTD + cat ${MOTD}.orig >> $MOTD +fi + +# Create statistics directory +test ! -e $STATS_DIR && mkdir -p $STATS_DIR + +# Check if server has rebooted (current uptime < stored uptime +if [ -s $STATS_DIR/uptime ]; then + if [ $(cut -d '.' -f 1 < /proc/uptime) -lt $(cat $STATS_DIR/uptime) ]; then + report "CRITICAL: System has rebooted." + fi +fi + +# Update uptime statistics +test -e /proc/uptime && cut -d '.' -f 1 < /proc/uptime > $STATS_DIR/uptime + + +# Removing temporary files +rm -f ${COLL_PROC} ${COLL_PROC}.tmp +rm -f ${COLL_MEM} ${COLL_MEM}.tmp +rm -f ${COLL_DISK} ${COLL_DISK}.tmp +rm -f ${COLL_EMPTY} + +if [ ! -z "$EMAIL" ]; then + + # Check if mail command is present in $PATH + if [ $(which mail 2>/dev/null >/dev/null && echo OK || echo NOK) != "OK" ]; then + echo "WARNING: unable to find mail command in \$PATH" + else + if [ ! -z "$(echo -e "$REPORT" | egrep -i "warning:|critical:")" ]; then + echo -e "$REPORT" | mail "$EMAIL" -s "MOTDstat alert" + fi + fi +fi diff --git a/etc/motds32/motds32.conf b/etc/motds32/motds32.conf new file mode 100644 index 0000000..5708bac --- /dev/null +++ b/etc/motds32/motds32.conf @@ -0,0 +1,60 @@ +# When the EMAIL is set, the motds32 will send e-mail notification when +# some event occures. Requires the mail command installed +EMAIL="root" + + +# Disk warning limit in percent [%] +# The default warning limit is to 80% +DISK_USAGE_WARNING=80 + +# Disk critical limit in percent [%] +# The default critical limit is to 90% +DISK_USAGE_CRITICAL=90 + +# CPU load warning limit in percent [%] +# The default warning limit is to 90% +CPU_WARNING=90 + +# CPU load critical limit in percent [%] +# The default critical limit is to 100% +CPU_CRITICAL=100 + +# Usage of SWAP space warning limit in percent [%] +# The default warning limit is set to 10% +SWAP_USAGE_WARNING=10 + +# Usage of SWAP space critical limit in percent [%] +# The default critical limit is set to 30% +SWAP_USAGE_CRITICAL=30 + +# Mail Queue size warning limit. Number of e-mails in mailq. By default the +# mailq checking is disabled (set to 0) +MAILQ_WARNING=5 + +# Limit the report to maximum number of n lines (including 2 rows header) +# Default is set to 15 rows. +MAX_ROWS_LIMIT=15 + +# Check if NTP is synchronizes from NTP sources [YES/NO]. +# Default is set to "NO" +CHECK_NTP="NO" + +# Enable reporting to local SYSLOG. All e-mail notifications will be also sent +# to system logger. (Default set to "NO" - disabled) +ENABLE_SYSLOG="NO" + +# Execute command when CPU load treshold was breached +# Example: LOAD_TRESHOLD_EXEC="top -b -n 1 | head -n 15" +LOAD_TRESHOLD_EXEC="top -b -n 1 | head -n 15" + +# Execute command when Disk usage treshold was breached +# Example: DISK_TRESHOLD_EXEC="df -h" +DISK_TRESHOLD_EXEC="df -h" + +# Execute command when SWAP usage treshold was breached +# Example: SWAP_TRESHOLD_EXEC="top -b -n 1 | head -n 15" +SWAP_TRESHOLD_EXEC="" + +# Execute command when NTP sychronisation problem occures +# Example: NTP_PROBLEM_EXEC="ntpq -p" +NTP_PROBLEM_EXEC=""