first commit

This commit is contained in:
Tomasz Kapias 2023-02-07 16:10:46 +07:00
commit d102b513e2
Signed by: tkapias
SSH key fingerprint: SHA256:bsmasrX7y0xxAHa/x1x8zAgHInO4nPpKMk5JIQ0Vsbw
13 changed files with 1407 additions and 0 deletions

121
00-motdfetch Executable file
View file

@ -0,0 +1,121 @@
#!/usr/bin/env bash
#
# MOTDfetch is a modular and dynamic MOTD replacement
# and a very nice command line information tool for Debian/Ubuntu systems.
#
# repository: https://git.tkapias.net/tkapias/MOTDfetch
# author: Tomasz Kapias
# email: tomasz@tkapias.net
#
# modules path: ./motdfetch.d/
# config paths: $HOME/.config/motdfetch/motdfetch.conf
# /etc/motdfetch/motdfetch.conf
#
# requirements (modules apart): sudo apt install coreutils bc
# locale env
unset LC_ALL
export LC_MESSAGES=C
# GLOBAL COLORS ######################################################
c_txt="39"
c_txt_emphase="35"
c_txt_deco="97"
c_txt_invert="30"
c_bg="47"
c_danger="31"
c_warning="33"
c_success="32"
c_title="${c_bg};1;${c_txt_invert}m"
# RUN OPTIONS #######################################################
# MODTfetch directory path
SCRIPT_DIR=$(dirname "$(realpath "$0")")
Help()
{
echo -e "\e[1;${c_txt_emphase}mMOTDfetch\e[1;${c_txt}m is a modular and dynamic MOTD replacement and a very"
echo -e "nice command line information tool for Debian/Ubuntu systems.\e[0m"
echo
echo "Syntax: bash $SCRIPT_DIR/00-motdfetch [-h] [-f] [-t ARG] [-l]"
echo
echo "options: -h Print this Help"
echo " -f force execution under heavy load"
echo " -t execute a standalone module with internal sample conf"
echo " -l list available modules files names"
}
unset option_force_load
# command options and arguments
while getopts "hft:l" option; do
case $option in
h) Help
exit ;;
f) option_force_load=1;;
t) # standalone module option
if [[ -f "$SCRIPT_DIR/motdfetch.d/$OPTARG" ]]; then
(. "$SCRIPT_DIR/motdfetch.d/$OPTARG")
exit 0
else
echo -e "\e[1;${c_danger}mError\e[0m: Invalid module filename, check the -l option.\n"
Help
exit 1
fi;;
l) # List modules option
for file in $SCRIPT_DIR/motdfetch.d/*
do
echo -e "$(basename $file) $(head -3 $file|tail -1)"
done | column -t -s '#'
exit 0;;
\?) # Invalid option
echo -e "\e[1;${c_danger}mError\e[0m: Invalid option"
Help
exit 1;;
esac
done
# CONFIG LOADING ###################################################
userconf="$HOME/.config/motdfetch/motdfetch.conf"
sysconf="/etc/motdfetch/motdfetch.conf"
if [[ -f "$userconf" ]];then
. "$userconf"
elif [[ -f "$sysconf" ]];then
. "$sysconf"
else
echo -e "\e[1;${c_danger}mError\e[0m: no configuration available.\n"
Help
exit 1
fi
# EXECUTION #########################################################
## load check
cores=$(/usr/bin/grep -ioPc 'processor\t:' /proc/cpuinfo 2>/dev/null)
if [[ "$cores" -eq "0" ]]; then
cores=1
fi
threshold="${cores:-1}.5"
echo -e "\n\e[${c_title} MOTDFETCH \e[0m\n"
if [[ $(echo "`cut -f1 -d ' ' /proc/loadavg` < $threshold" | /usr/bin/bc) -eq 1 || $option_force_load -eq 1 ]]; then
### output modules
for file in $SCRIPT_DIR/motdfetch.d/*
do
(. "$file")
done
echo
else
### too much load for modules, run only 00 & 99 + msg
module_header_disable=0
(. $SCRIPT_DIR/motdfetch.d/00-*)
module_userslog_disable=0
(. $SCRIPT_DIR/motdfetch.d/99-*)
echo -e "\n\e[${c_title} WE SKIPPED SOME MODULES \e[0m\n"
echo -e " MOTDfetch skipped some modules because of\n this \e[1;${c_danger}msystem load\e[0m being higher than \e[1;${c_danger}m$threshold\e[0;${c_warning}m.\e[0m\n"
echo -e " \e[1;${c_txt}mBAD ADVICE\e[0m: you can force execution with the option [-f]:\n # bash $SCRIPT_DIR/00-motdfetch -f\n"
fi

BIN
docs/img/preview.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

157
install.sh Executable file
View file

@ -0,0 +1,157 @@
#!/usr/bin/env bash
#
# installation script for MOTDfetch
# repository: https://git.tkapias.net/tkapias/MOTDfetch
# author: Tomasz Kapias
# email: tomasz@tkapias.net
#
# run with sudo or as root user, or directly from the repository:
# sudo wget -q https://git.tkapias.net/tkapias/MOTDfetch/raw/master/install.sh -O - | sudo bash
# locale
export LC_ALL="C.UTF-8"
export GREP_COLORS="ms=01;32:mc=01;32:sl=:cx=:fn=35:ln=31:bn=31:se=36"
N="\e[0m"
T="\e[1;35m"
S="\e[0;32m"
W="\e[0;33m"
E="\e[0;31m"
# check if executed by root or sudo
if (( ! `id -u` == 0 )); then
echo -e "${E}Please do run as root or with sudo${N}"
exit
fi
# check if the user uses Bash
if [[ ! $BASH_VERSION ]]; then
echo -e "\n\e[1;33mThis tool is written for Bash, its compatibility is not tested with other shells.\e[0m"
echo -e "You are currently using \e[1;31m $SHELL\e[0m, do you want to continue? [\e[1;32mY\e[0m/\e[1;31mn\e[0m]\n"
read -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
ERRORCOUNT=0
if [[ -f "/etc/ssh/sshd_config" ]]; then
echo -e "${T}# setup ssh server (/etc/ssh/sshd_config) to not print MOTD or LASTLOGIN${N}"
sed -ri 's/^(\#?)(PrintMotd|PrintLastLog)([[:space:]]+)(yes|no)/\2\3no/' /etc/ssh/sshd_config
resultsshno=$(/usr/bin/grep -E "PrintMotd\s+(no)?|\PrintLastLog\s+(no)?" /etc/ssh/sshd_config 2>/dev/null)
if [[ $resultsshno == "" ]]; then
echo -e "${E}FAIL - check manually \"PrintMotd no\" & \"PrintLastLog no\"${N}\n"
((ERRORCOUNT++))
else
echo -e "$resultsshno" | /usr/bin/grep --color=auto "no"
echo -e "${S}DONE${N}\n"
fi
echo -e "${T}# setup ssh server (/etc/ssh/sshd_config) to accept custom SSH_MOTD env variable${N}"
sed -ri '/SSH_MOTD/! s/^(\#?)(AcceptEnv)(.+)/\2\3 SSH_MOTD/' /etc/ssh/sshd_config
resultsshmotd=$(/usr/bin/grep -E "^AcceptEnv.+SSH_MOTD" /etc/ssh/sshd_config 2>/dev/null)
if [[ $resultsshmotd == "" ]]; then
echo -e "${E}FAIL - check manually \"AcceptEnv SSH_MOTD\"${N}\n"
((ERRORCOUNT++))
else
echo -e "$resultsshmotd" | /usr/bin/grep --color=auto "SSH_MOTD"
echo -e "${S}DONE${N}\n"
fi
else
echo -e "${E}/etc/ssh/sshd_config DOES NOT EXIST${N}\n"
((ERRORCOUNT++))
fi
echo -e "${T}# setup PAM's sshd (/etc/pam.d/sshd) to not print MOTD and MAIL${N}"
if [[ -f "/etc/pam.d/sshd" ]]; then
sed -ri 's/^(\#?)(session[[:space:]]+optional[[:space:]]+)(pam_motd.so|pam_mail.so)(.+)/#\2\3\4/' /etc/pam.d/sshd
resultpamcom=$(/usr/bin/grep -E "^\#.+pam_motd.so|^\#.+pam_mail.so" /etc/pam.d/sshd 2>/dev/null)
if [[ $resultpamcom == "" ]]; then
echo -e "${E}FAIL - comment manually lines with \"pam_motd.so\" & \"pam_mail.so\"${N}\n"
((ERRORCOUNT++))
else
echo -e "$resultpamcom" | /usr/bin/grep --color=auto -E "^\#"
echo -e "${S}DONE${N}\n"
fi
else
echo -e "${E}FAIL - /etc/pam.d/sshd DOES NOT EXIST${N}\n"
((ERRORCOUNT++))
fi
echo -e "${T}# restart ssh server if it's active${N}"
if [[ $(systemctl is-active ssh 2>/dev/null) == "active" ]]; then
systemctl restart ssh.service
echo -e "${S}DONE${N}\n"
else
echo -e "${E}SSHD NOT ACTIVE${N}\n"
fi
echo -e "${T}# install minimal dependencies${N}"
apt install -f figlet coreutils bc wget nano dnsutils openssl s-nail
echo
echo -e "${T}# install ANSI_Shadow figlet font${N}"
downloadfigletfont=$(wget -nv -O "/usr/share/figlet/ANSI Shadow.flf" https://raw.githubusercontent.com/xero/figlet-fonts/master/ANSI%20Shadow.flf)
if $downloadfigletfont ; then
echo -e "${S}DONE${N}\n"
else
echo -e "${E}FAIL${N}\n"
((ERRORCOUNT++))
fi
echo -e "${T}# check directories & backup old & obsolete files${N}"
if [[ ! -d "/etc/update-motd.d/backup" ]]; then
mkdir -vp /etc/update-motd.d/backup
fi
if [[ ! -d "/etc/motdfetch" ]]; then
mkdir -vp /etc/motdfetch
fi
chmod -v 755 /etc/update-motd.d/ /etc/update-motd.d/backup/ 2>/dev/null
echo -e "${S}CHMOD DONE${N}\n"
if [[ -d "/etc/update-motd.d/motdfetch.d" ]]; then
cp -p -Rv /etc/update-motd.d/*motdfetch* /etc/update-motd.d/backup/ 2>/dev/null
echo -e "${S}MOTDfetch BACKUP DONE${N}"
fi
if [[ -d "/etc/motd" ]]; then
mkdir -vp /etc/update-motd.d/backup/etc
mv -vf /etc/motd /etc/update-motd.d/backup/etc/motd 2>/dev/null
echo -e "${S}Static MOTD BACKUP DONE${N}"
fi
if [[ -d "/etc/update-motd.d/10-uname" ]]; then
mv -vf /etc/update-motd.d/10-uname /etc/update-motd.d/backup/10-uname 2>/dev/null
echo -e "${S}Debian uname MOTD BACKUP DONE${N}"
fi
echo
echo -e "${T}# install/update MOTDfetch${N}"
downloadmotd=$(wget -c -nv https://git.tkapias.net/tkapias/MOTDfetch/archive/master.tar.gz -O - \
| tar -xz --strip-components=1 -C /etc/update-motd.d/ MOTDfetch-master/motdfetch.d/ MOTDfetch-master/00-motdfetch MOTDfetch-master/motdfetch.sample.conf 2>/dev/null)
if $downloadmotd ; then
mv -vf /etc/update-motd.d/motdfetch.sample.conf /etc/motdfetch/motdfetch.sample.conf
if [[ -f "/etc/motdfetch/motdfetch.conf" ]]; then
echo -e "${S}SUCCESSFUL UPDATE${N}\n"
else
cp -p -Rv /etc/motdfetch/motdfetch.sample.conf /etc/motdfetch/motdfetch.conf
echo -e "${S}SUCCESSFUL INSTALLATION${N}\n"
fi
if (( $ERRORCOUNT == 1 )); then
echo -e "${W}But CAUTION, $ERRORCOUNT configuration steps have failed.${N} Please check if you need to adjust manually.\n"
elif (( $ERRORCOUNT > 1 )); then
echo -e "${W}But CAUTION, $ERRORCOUNT configuration step has failed.${N} Please check if you need to adjust manually.\n"
fi
echo -e " - ${S}edit the config file${N}:
# sudo nano /etc/motdfetch/motdfetch.conf
${S}or create a new user config${N}:
# userdir=~/.config/motdfetch \\
&& mkdir \$userdir \\
&& cp /etc/motdfetch/motdfetch.sample.conf \$userdir/motdfetch.conf \\
&& nano \$userdir/motdfetch.conf\n"
echo -e " - ${S}add an alias \"motd\" command to your .bashrc${N}:\n alias motd='/etc/update-motd.d/00-motdfetch'\n"
echo -e " - ${S}how to use MOTDfetch as an alternative MOTD for SSH sessions${N}:
+ ${S}on the ssh server, add the new \"motd\" alias at the end of the ssh user .bashrc${N}:
if [[ -n \$SSH_CONNECTION ]]; then\n motd\n fi
+ ${S}on the ssh client, add an alias \"sshmotd\" to your .bashrc${N}:\n alias sshmotd='ssh -o SetEnv=SSH_MOTD=1'\n"
else
echo -e "${E}INSTALLATION FAILED - please retry${N}\n"
fi

81
motdfetch.d/00-header Normal file
View file

@ -0,0 +1,81 @@
#!/usr/bin/env bash
#
# display a custom decorative text (default to username)
# + welcome the username + date, time and timezone
# + inform of mail presence for user (if s-nail is installed)
#
# requirements: sudo apt install figlet
# sudo wget --no-check-certificate -O "/usr/share/figlet/ansi-shadow.flf" https://raw.githubusercontent.com/xero/figlet-fonts/master/ANSI%20Shadow.flf
# optionnal: sudo apt install s-nail
# GENERAL ###########################################################
# locale env
unset LC_ALL
export LC_MESSAGES=C
# check if module was disabled
module_disable=${module_header_disable:=0}
if (($module_disable == 1)); then
exit 1
fi
# colors
c_txt=${c_txt:="39"}
c_txt_emphase=${c_txt_emphase:="35"}
c_txt_deco=${c_txt_deco:="97"}
c_txt_invert=${c_txt_invert:="30"}
c_bg=${c_bg_sec:="47"}
c_danger=${c_danger:="31"}
c_warning=${c_warning:="33"}
c_success=${c_success:="32"}
c_title=${c_title:="${c_bg};1;${c_txt_invert}m"}
# OPTIONS ###########################################################
# banner custom text or username
username=$(/usr/bin/id -un)
header_text=${module_header_text:="$username"}
# PREPARATIONS ######################################################
# check if s-nail is available and if you have mail
if command -v s-nail 1>/dev/null; then
# check mail for user
if /usr/bin/s-nail -e; then
hasmail=$(echo -e " You have \e[1;${c_success}mmessages\e[0m.")
else
hasmail=$(echo -e " You have no messages.")
fi
else
hasmail=""
fi
# trim banner text
header_text_width=${#header_text}
figlet_width=$(/usr/bin/env figlet -f "ANSI Shadow" "$header_text" | wc -L)
while [[ $figlet_width -ge 73 ]]; do
let "header_text_width-=1"
header_text=$(echo -e "${header_text:0:$header_text_width}...")
figlet_width=$(/usr/bin/env figlet -f "ANSI Shadow" "$header_text" | wc -L)
done
# OUTPUT ############################################################
if [[ $module_header_text != "$username" ]]; then
/usr/bin/env figlet -f "ANSI Shadow" "$header_text" \
| sed "$d" \
| sed "s/^/ /" \
| echo -e "$(sed "1 s/^/\\\e[${c_txt_emphase}m/")" \
| echo -e "$(sed "$ s/$/\\\e[0m/")"
echo -e " \e[1;${c_txt}mWelcome \e[${c_txt_emphase}m${username}\e[${c_txt}m, it is \e[${c_txt_emphase}m$(date +"%Y-%m-%d %H:%M:%S %z")\e[${c_txt}m.\e[0m\n${hasmail}"
else
echo -e " Welcome"
/usr/bin/env figlet -f "ANSI Shadow" "$header_text" \
| sed "$d" \
| sed "s/^/ /" \
| echo -e "$(sed "1 s/^/\\\e[${c_txt_emphase}m/")" \
| echo -e "$(sed "$ s/$/\\\e[0m/")"
echo -e " \e[1;${c_txt}mIt is \e[${c_txt_emphase}m$(date +"%Y-%m-%d %H:%M:%S %z")\e[${c_txt}m.\e[0m\n${hasmail}"
fi

149
motdfetch.d/01-sysinfo Normal file
View file

@ -0,0 +1,149 @@
#!/usr/bin/env bash
#
# display a list of system informations
# + dig and curl have shorts timeouts to not hang the script
#
# requirements: sudo apt install dnsutils
# GENERAL ###########################################################
# locale env
unset LC_ALL
export LC_MESSAGES=C
# check if module was disabled
module_disable=${module_sysinfo_disable:=0}
if (($module_disable == 1)); then
exit 1
fi
# colors
c_txt=${c_txt:="39"}
c_txt_emphase=${c_txt_emphase:="35"}
c_txt_deco=${c_txt_deco:="97"}
c_txt_invert=${c_txt_invert:="30"}
c_bg=${c_bg_sec:="47"}
c_danger=${c_danger:="31"}
c_warning=${c_warning:="33"}
c_success=${c_success:="32"}
c_title=${c_title:="${c_bg};1;${c_txt_invert}m"}
# api website name for plain public IP4/6 check
publicip_api=${global_publicip_api:="icanhazip.com"}
# PREPARATIONS ######################################################
# output header
echo -e "\nWaiting for DNS and IP checks\e[5m...\e[0m"
# get hostname
HOSTNAME=`hostname -f`
# get distribution name
DISTRO=`/usr/bin/cat /etc/*release | /usr/bin/grep "PRETTY_NAME" | cut -d "=" -f 2- | sed 's/"//g'`
# get name of main interface with a route for internet
INTERNETIFACE="$(ip -o route get 8/32 2>/dev/null)"
if [[ "$INTERNETIFACE" ]]; then
INTERNETIFACE=`echo -e "$INTERNETIFACE" | sed -e 's/.*dev //' -e 's/ .*//'`
else
INTERNETIFACE="\e[1;${c_warning}mno interface with a route to 8/32\e[0;${c_txt}m"
fi
# get DNS servers ips from resolv.conf
DNSSERVERS=`/usr/bin/grep '^nameserver' /etc/resolv.conf | cut -d' ' -f2 | paste -sd ','`
if [[ ! $DNSSERVERS ]];then
DNSSERVERS="\e[1;${c_warning}mno ns in resolv.conf\e[0;${c_txt}m"
fi
# get current output public IPs and their revers DNS record
PUBLICIP4=`timeout 6 curl -s -4 $publicip_api 2>/dev/null`
if [[ $PUBLICIP4 ]];then
IP4PTR=`timeout 4 dig +short -x $PUBLICIP4 @1.1.1.1 2>/dev/null`
if [[ ! $IP4PTR ]];then
IP4PTR="\e[0;${c_txt}mno data"
fi
else
IP4PTR="\e[0;${c_txt}mno data"
PUBLICIP4="\e[1;5;${c_warning}mno IPv4 internet\e[0;${c_txt}m"
INTERNET_IP4=0
fi
PUBLICIP6=`timeout 6 curl -s -6 $publicip_api 2>/dev/null`
if [[ $PUBLICIP6 ]];then
IP6PTR=`timeout 4 dig +short -x $PUBLICIP6 @1.1.1.1 2>/dev/null`
if [[ ! $IP6PTR ]];then
IP6PTR="\e[0;${c_txt}mno data"
fi
else
IP6PTR="\e[0;${c_txt}mno data"
PUBLICIP6="\e[1;${c_warning}mno ipv6 internet\e[0;${c_txt}m"
INTERNET_IP6=0
fi
INTERNET_OUT=0
if [[ $INTERNET_IP4 && $INTERNET_IP6 ]]; then
INTERNET_OUT=1
fi
# get DNS records of the FQDN
HOSTNAMEIP4=`timeout 4 dig +short A $HOSTNAME @1.1.1.1 2>/dev/null`
if [[ ! $HOSTNAMEIP4 ]];then
HOSTNAMEIP4="\e[0;${c_txt}mno data"
fi
HOSTNAMEIP6=`timeout 4 dig +short AAAA $HOSTNAME @1.1.1.1 2>/dev/null`
if [[ ! $HOSTNAMEIP6 ]];then
HOSTNAMEIP6="\e[0;${c_txt}mno data"
fi
# get processes
PROCESS=`/usr/bin/ps -eo user= | sort | uniq -c | awk '{ print $2 " " $1 }'`
PROCESS_ALL=`echo "$PROCESS" | awk {'print $2'} | awk '{ SUM += $1} END { print SUM }'`
PROCESS_ROOT=`echo "$PROCESS" | /usr/bin/grep root | awk {'print $2'}`
PROCESS_USER=`echo "$PROCESS" | /usr/bin/grep -v root | awk {'print $2'} | awk '{ SUM += $1} END { print SUM }'`
# get processors
PROCESSOR_NAME=`/usr/bin/grep "model name" /proc/cpuinfo | cut -d ' ' -f3- | awk {'print $0'} | head -1 | sed -e 's/([^()]*)/ /g' -e 's/\s\+/ /g'`
PROCESSOR_COUNT=`/usr/bin/grep -ioPc 'processor\t:' /proc/cpuinfo 2>/dev/null`
# get load averages
IFS=" " read LOAD1 LOAD5 LOAD15 <<<$(cat /proc/loadavg | awk '{ print $1,$2,$3 }')
if [[ $(echo "$LOAD1 > $(echo "$PROCESSOR_COUNT + ($PROCESSOR_COUNT / 3)" | bc -l )" | bc) == 1 ]]; then
LOAD1="\e[1;5;${c_danger}m$LOAD1\e[0;${c_txt}m"
elif [[ $(echo "$LOAD1 > $(echo "$PROCESSOR_COUNT - ($PROCESSOR_COUNT / 3)" | bc -l )" | bc) == 1 ]]; then
LOAD1="\e[1;5;${c_warning}m$LOAD1\e[0;${c_txt}m"
fi
if [[ $(echo "$LOAD5 > $(echo "$PROCESSOR_COUNT * 2" | bc -l )" | bc) == 1 ]]; then
LOAD2="\e[1;5;${c_warning}m$LOAD5\e[0;${c_txt}m"
fi
if [[ $(echo "$LOAD15 > $(echo "$PROCESSOR_COUNT * 2" | bc -l )" | bc) == 1 ]]; then
LOAD3="\e[1;5;${c_warning}m$LOAD15\e[0;${c_txt}m"
fi
# get free memory
IFS=" " read USED AVAIL TOTAL <<<$(free -htm | /usr/bin/grep "Mem" | awk {'print $3,$7,$2'})
# get free swap
IFS=" " read SUSED SAVAIL STOTAL <<<$(free -htm | /usr/bin/grep "Swap" | awk {'print $3,$4,$2'})
# OUTPUT ############################################################
W="\e[0;${c_txt}m"
X="\e[1;${c_txt}m"
G="\e[1;${c_txt_emphase}m"
N="\e[0m"
echo -e "\e[1A\e[${c_title} System information $N
$X Distro$W..........: $DISTRO
$X Kernel$W..........: `uname -sr`
$X Uptime$W..........: $G`uptime -p`$W
$X CPU$W.............: $PROCESSOR_NAME
$X CPU cores$W.......: $G$PROCESSOR_COUNT$W
$X Load$W............: $G$LOAD1$W (1m), $G$LOAD5$W (5m), $G$LOAD15$W (15m)
$X Processes$W.......: $G$PROCESS_ROOT$W (root), $G$PROCESS_USER$W (user), $G$PROCESS_ALL$W (total)
$X Memory$W..........: $G$USED$W used, $G$AVAIL$W avail, $G$TOTAL$W total$N"
if [[ ${SUSED} ]]; then
echo -e "$X Swap$W............: $G$SUSED$W used, $G$SAVAIL$W avail, $G$STOTAL$W total$W"
else
echo -e "$X Swap$W............: \e[1;${c_warning}mno Swap$W"
fi
echo -e "
$X Hostname/FQDN$W...: $G$(hostname)$W / $G$(hostname -f)$W
$X Internet Iface$W..: $G$INTERNETIFACE$W
$X DNS servers$W.....: $G$(echo -e "${DNSSERVERS}" | sed "s/,/\\\e[0m, \\\e[1;${c_txt_emphase}m/g")$N"
if [[ ${INTERNET_OUT} == 0 ]]; then
echo -e "$X Public IP4/IP6$W..: $G$PUBLICIP4$W / $G$PUBLICIP6$N"
else
echo -e "$X Public IP4/IP6$W..: \e[1;${c_warning}mno internet or "${publicip_api}" timeout$N"
fi
echo -e "$X PTR IP4/IP6$W.....: $G$IP4PTR$W / $G$IP6PTR$W
$X FQDN A/AAAA$W.....: $G$HOSTNAMEIP4$W / $G$HOSTNAMEIP6$N"

143
motdfetch.d/02-services Normal file
View file

@ -0,0 +1,143 @@
#!/usr/bin/env bash
#
# check status of a list of systemd service units
# + accept service name with or without extension (.service)
# + default to system wide services but accept --user services with the :uid
# + alarm by bullet coloration and blinking
#
# requirements: systemd
# GENERAL ###########################################################
# locale env
unset LC_ALL
export LC_MESSAGES=C
# check if module was disabled
module_disable=${module_services_disable:=0}
if (($module_disable == 1)); then
exit 1
fi
# colors
c_txt=${c_txt:="39"}
c_txt_emphase=${c_txt_emphase:="35"}
c_txt_deco=${c_txt_deco:="97"}
c_txt_invert=${c_txt_invert:="30"}
c_bg=${c_bg_sec:="47"}
c_danger=${c_danger:="31"}
c_warning=${c_warning:="33"}
c_success=${c_success:="32"}
c_title=${c_title:="${c_bg};1;${c_txt_invert}m"}
# OPTIONS ###########################################################
# array of services
if [[ $module_services ]]; then
services=(${module_services[@]})
else
services=("cron.service" "dbus.service:1000" "user2service:1002")
fi
IFS=$'\n' services=($(sort <<<"${services[*]}"))
unset IFS
# column max-width
width=${module_services_width:="80"}
# PREPARATIONS ######################################################
# get status
# + dim status if the service does not exist or if the user is not root and the service has another uid
serviceStatus=()
for service in "${services[@]}"; do
uid=${service/*:/}
# script executed as root and the service to check is from a user
if [[ $service = *:* ]] && [[ "$(id -u)" == "0" ]]; then
service=${service/.service/}
service=${service/:[0-9]*/}
serviceexist=$(machinectl -q shell --uid="$uid" .host /usr/bin/systemctl list-units --user -q --plain --no-pager --full --all -t service 2>/dev/null \
| awk '$1 ~ /\.service$/ { sub("\\.service$", "", $1); print $1 }' \
| /usr/bin/grep "$service")
if [[ "$serviceexist" ]] ;then
status=$(machinectl -q shell --uid="$uid" .host /usr/bin/systemctl is-active --user "$service")
if [[ $status = inactive* ]]; then
serviceStatus+=("inactive")
elif [[ $status = active* ]]; then
serviceStatus+=("active")
else
serviceStatus+=("hidden")
fi
else
serviceStatus+=("hidden")
fi
# script executed by the same user as the service user
elif [[ $service = *:* ]] && [[ "$(id -u)" == "$uid" ]]; then
service=${service/.service/}
service=${service/:[0-9]*/}
serviceexist=$(/usr/bin/systemctl list-units --user -q --plain --no-pager --full --all -t service 2>/dev/null \
| awk '$1 ~ /\.service$/ { sub("\\.service$", "", $1); print $1 }' \
| /usr/bin/grep "$service")
if [[ "$serviceexist" ]] ;then
serviceStatus+=($(/usr/bin/systemctl --user is-active "$service"))
else
serviceStatus+=("hidden")
fi
# script executed by another user than root and than the service user
elif [[ $service = *:* ]]; then
serviceStatus+=("hidden")
# system service
else
service=${service/.service/}
service=${service/:[0-9]*/}
serviceexist=$(/usr/bin/systemctl list-units -q --plain --no-pager --full --all -t service 2>/dev/null \
| awk '$1 ~ /\.service$/ { sub("\\.service$", "", $1); print $1 }' \
| /usr/bin/grep "$service")
if [[ "$serviceexist" ]] ;then
serviceStatus+=($(/usr/bin/systemctl is-active "$service"))
else
serviceStatus+=("hidden")
fi
fi
done
# OUTPUT ############################################################
# output header
echo -e "\n\e[${c_title} Services checks \e[0m\n"
# output checklist
line=" "
lineLen=1
for i in "${!serviceStatus[@]}"
do
service=${services[$i]/.service/}
service=${service^}
# Next line and next line length
next=" $service "
nextLen=$((1+${#next}))
# If the current line will exceed the max column with then echo and start a new line
if [[ $((lineLen+nextLen)) -gt $width ]]; then
echo -e "$line"
lineLen=1
line=" "
fi
lineLen=$((lineLen+nextLen))
# Color the next line green if it's active, red if inactive, else orange
if [[ "${serviceStatus[$i]}" == "active" ]]
then
line+=" \e[${c_success}m●\e[0m \e[${c_txt}m$service\e[0m "
elif [[ "${serviceStatus[$i]}" == "inactive" ]]
then
line+=" \e[1;5;${c_danger}m▲\e[0m \e[1;${c_txt}m$service\e[0m "
else
line+=" \e[1;2;${c_warning}m▲\e[0m \e[1;2;${c_txt}m$service\e[0m "
fi
done
# echo what is left
echo -e "$line"

227
motdfetch.d/03-filesystem Normal file
View file

@ -0,0 +1,227 @@
#!/usr/bin/env bash
#
# status of mounted filesystems in 2 steps
# 1. use "df" for general fs except zfs, squashfs, tmpfs, devtmpfs and overlay
# 2. use "zpool" for ZFS pools
# + displays name, filesystem type, used blocks and inodes or pool health when relevant
# + custom warn threshold for blocks
# + warn threshold for inodes (80%), flashing danger threshold for health, blocks (95%) and inodes (90%)
#
# requirements: sudo apt install coreutils
# optionnal: sudo apt install zfsutils-linux
# GENERAL ###########################################################
# locale env
unset LC_ALL
export LC_MESSAGES=C
# check if module was disabled
module_disable=${module_filesystem_disable:=0}
if (($module_disable == 1)); then
exit 1
fi
# colors
c_txt=${c_txt:="39"}
c_txt_emphase=${c_txt_emphase:="35"}
c_txt_deco=${c_txt_deco:="97"}
c_txt_invert=${c_txt_invert:="30"}
c_bg=${c_bg_sec:="47"}
c_danger=${c_danger:="31"}
c_warning=${c_warning:="33"}
c_success=${c_success:="32"}
c_title=${c_title:="${c_bg};1;${c_txt_invert}m"}
# OPTIONS ###########################################################
# max usage warning threshold
max_usage=${module_filesystem_max_usage:="85"}
# disable ZFS section
zfs_disable=${module_filesystem_zfs_disable:=0}
# PREPARATIONS ######################################################
# disk usage: ignore zfs, squashfs & tmpfs, standard error output because of a bug with /run/user/1000/doc
dfcmd=$(/usr/bin/df -H -x zfs -x squashfs -x tmpfs -x devtmpfs -x overlay --output 2>/dev/null)
mapfile -t dfoutput < <(echo "$dfcmd" | tail -n+2)
# determine best width for the bar
largesttarget=$(echo "$dfcmd" | awk '{if (length ($12)>max) max=length($12)} END {print max}')
if [[ $((largesttarget+30)) -gt 50 ]]; then
bar_width=$((largesttarget+30))
else
bar_width="50"
fi
# OUTPUT ############################################################
# general filesystems
linecounter=0
if [[ "${dfoutput[@]}" ]]; then
# output header
echo -e "\n\e[${c_title} Filesystem usage \e[0m\n"
# output filesystem status except ZFS
for line in "${dfoutput[@]}"; do
# parse df output
diskid=$(echo "$line" | awk '{print $12}')
diskused=$(echo "$line" | awk '{print $10}'| sed 's/%//')
disktotal=$(echo "$line" | awk '{print $7}')
fsformat=$(echo "$line" | awk '{print $2}')
inodesused=$(echo "$line" | awk '{print $6}'| sed 's/%//')
inodestotal=$(echo "$line" | awk '{print $3}')
# bar & blocks coloration, danger red above 95%
if [[ "${diskused}" -ge "95" ]]; then
barcolor="\e[1;${c_danger}m"
blockscolor="\e[1;${c_danger}m"
flashing="\e[5m"
elif [ "${diskused}" -ge "${max_usage}" ]; then
barcolor="\e[1;${c_warning}m"
blockscolor="\e[1;${c_warning}m"
flashing=""
else
barcolor="\e[1;${c_success}m"
blockscolor="\e[${c_txt}m"
flashing=""
fi
# print colored bar until used_width
used_width=$(((diskused*$bar_width)/100))
bar="[${barcolor}"
for ((i=0; i<$used_width; i++)); do
bar+="="
done
# print dimmmed bar until end
bar+="\e[${c_txt}m\e[0m"
for ((i=$used_width; i<$bar_width; i++)); do
bar+="="
done
bar+="\e[0m]"
# print blocks usage line & bar
padding=$((bar_width-28))
if [[ $linecounter -ge 1 ]]
then
echo -e "\n"
fi
echo "${line}" | printf "\e[1;${c_txt_emphase}m%-${padding}s\e[0m${blockscolor}%+3s%% used ${flashing}blocks\e[0m out of %+4s\n" $diskid $diskused $disktotal | sed 's/^/ /'
echo -e "${bar}" | sed 's/^/ /'
# print filesystem type & inodes usage line
if [[ ! "$inodesused" == "-" ]]
then
padding=$((bar_width-34))
# inodes coloration, danger red above 90%
if [[ "${inodesused}" -ge "90" ]]; then
inodescolor="\e[1;${c_danger}m"
flashing="\e[5m"
elif [[ "${inodesused}" -ge "80" ]]; then
inodescolor="\e[1;${c_warning}m"
flashing=""
else
inodescolor="\e[${c_txt}m"
flashing=""
fi
echo "${line}" | printf "Type: %-${padding}s${inodescolor}%+3s%% used ${flashing}inodes\e[0m out of %+4s\n" $fsformat $inodesused $inodestotal | sed 's/^/ /'
else
echo "${line}" | printf "Type: %-10s\n" $fsformat | sed 's/^/ /'
fi
linecounter=$((linecounter+1))
done
else
echo " no general filesystem available, ZFS expected in next section"
fi
# check if ZFS section was disabled
if [[ $zfs_disable == 1 ]]; then
exit 1
fi
# output ZFS header
echo -e "\n\e[${c_title} Zpool usage \e[0m\n"
# check if zpool is available
if ! command -v zpool 1> /dev/null; then
echo " no ZFS tools available"
exit 1
fi
# ZFS zpool usage & health status
zlistcmd=$(zpool list -o name,cap,size,health 2> /dev/null)
if [[ $zlistcmd == "no pools available" ]]; then
echo " no ZFS pool available"
else
mapfile -t zpools < <(echo "$zlistcmd" | tail -n+2)
# determine best width for the bar
largesttarget=$(echo "zlistcmd" | awk '{if (length ($1)>max) max=length($1)} END {print max}')
if [[ $((largesttarget+30)) -gt 50 ]]; then
bar_width=$((largesttarget+30))
else
bar_width="50"
fi
linecounter=0
for line in "${zpools[@]}"; do
# parse zpool list output
poolid=$(echo "$line" | awk '{print $1}')
poolused=$(echo "$line" | awk '{print $2}'| sed 's/%//')
pooltotal=$(echo "$line" | awk '{print $3}')
poolhealth=$(echo "$line" | awk '{print $4}')
# bar & pools coloration, danger red above 95%
if [[ "${poolused}" -ge "95" ]]; then
barcolor="\e[1;${c_danger}m"
poolscolor="\e[1;${c_danger}m"
flashing="\e[5m"
elif [[ "${poolused}" -ge "${max_usage}" ]]; then
barcolor="\e[1;${c_warning}m"
poolscolor="\e[1;${c_warning}m"
flashing=""
else
barcolor="\e[1;${c_success}m"
poolscolor="\e[${c_txt}m"
flashing=""
fi
# print colored bar until used_width
used_width=$(((poolused*$bar_width)/100))
bar="[${barcolor}"
for ((i=0; i<$used_width; i++)); do
bar+="="
done
# print dimmmed bar until end
bar+="\e[${c_txt}m\e[0m"
for ((i=$used_width; i<$bar_width; i++)); do
bar+="="
done
bar+="\e[0m]"
# print pool usage line & bar
padding=$((bar_width-29))
if [[ $linecounter -ge 1 ]]
then
echo -e "\n"
fi
echo "${line}" | printf "\e[1;${c_txt_emphase}m%-${padding}s\e[0m${poolscolor}%+3s%% used ${flashing}blocks\e[0m out of %+5s\n" $poolid $poolused $pooltotal | sed 's/^/ /'
echo -e "${bar}" | sed 's/^/ /'
# print pool health line
padding=$((bar_width-34))
# health coloration, danger red for FAULTED
if [[ "${poolhealth}" == "FAULTED" ]]; then
healthcolor="\e[1;${c_danger}m"
flashing="\e[5m"
elif [[ "${poolhealth}" == "OFFLINE" ]] || [[ "${poolhealth}" == "UNAVAIL" ]] || [[ "${poolhealth}" == "REMOVED" ]] || [[ "${poolhealth}" == "DEGRADED" ]]; then
healthcolor="\e[1;${c_warning}m"
flashing=""
else
healthcolor="\e[${c_txt}m"
flashing=""
fi
padding=$((bar_width-17))
text="health"
echo "${line}" | printf "%+${padding}s status: ${healthcolor}${flashing}%+8s\e[0m\n" $text ${poolhealth,,} | sed 's/^/ /'
linecounter=$((linecounter+1))
done
fi

119
motdfetch.d/04-tls Normal file
View file

@ -0,0 +1,119 @@
#!/usr/bin/env bash
#
# check X.509 cert for a domain:port with openssl, for correspondance and expiration
# + by default the port is 443
# + there is a timeout of 6 sec and is there is no connectivity the check is skipped
#
# requirements: sudo apt install openssl
# GENERAL ###########################################################
# locale env
unset LC_ALL
export LC_MESSAGES=C
# check if module was disabled
module_disable=${module_tls_disable:=0}
if (($module_disable == 1)); then
exit 1
fi
# colors
c_txt=${c_txt:="39"}
c_txt_emphase=${c_txt_emphase:="35"}
c_txt_deco=${c_txt_deco:="97"}
c_txt_invert=${c_txt_invert:="30"}
c_bg=${c_bg_sec:="47"}
c_danger=${c_danger:="31"}
c_warning=${c_warning:="33"}
c_success=${c_success:="32"}
c_title=${c_title:="${c_bg};1;${c_txt_invert}m"}
# api website name for plain public IP4/6 check
publicip_api=${global_publicip_api:="icanhazip.com"}
# OPTIONS ###########################################################
# domains:ports input
if [[ $module_tls_domains ]]
then
tls_domains=(${module_tls_domains[@]})
else
tls_domains=("www.google.com" "smtp.gmail.com:465")
fi
IFS=$'\n' tls_domains=($(sort <<<"${tls_domains[*]}"))
unset IFS
# PREPARATIONS ######################################################
# print loading message
echo -e "\nWaiting for TLS checks\e[5m...\e[0m"
# parse output
output=" \e[1;4;${c_txt}mDomain\e[24m|\e[4mPort\e[24m|\e[4mValid until\e[0m"
currentTime=$(date +%s)
# check if there is internet connectivity
INTERNET_OUT=0
PUBLICIP="$(timeout 6 curl -s $publicip_api 2>/dev/null)"
if [[ ! $PUBLICIP ]]; then
INTERNET_OUT=1
fi
# OUTPUT ############################################################
for domain in "${tls_domains[@]}"
do
if [[ $domain == *:* ]]
then
port=${domain/*:/}
domain=${domain/:*/}
else
port="443"
fi
if [[ $INTERNET_OUT == 0 ]]; then
# fetch cert
cert=""
cert=$(timeout 6 openssl s_client -servername ${domain} -connect ${domain}:${port} < /dev/null 2>/dev/null)
# fetch subject name with a 3 sec timeout
certSubj=$(echo -e "$cert" | openssl x509 -noout -subject 2>/dev/null)
# fetch expiration time
certTime=$(echo -e "$cert" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
certLineTime=$(date -d "${certTime}" +"%F")
certTimestamp=$(date -d "${certTime}" +%s)
port="\e[${c_txt}m${port}\e[0m"
# check subject name with a 3 sec timeout
if [[ $certSubj == *${domain} ]]
then
domain="\e[${c_txt}m${domain}\e[0m"
# check expiration time - 3 days
if [[ "$((${certTimestamp} - 259200 ))" -ge "${currentTime}" ]]
then
sign="\e[${c_success}m●\e[0m"
result="\e[1;${c_success}m$certLineTime\e[0m"
# check expiration time today
elif [[ "${certTimestamp}" -ge "${currentTime}" ]]
then
sign="\e[1;${c_warning}m▲\e[0m"
result="\e[1;${c_warning}m${certLineTime}\e[0m"
else
sign="\e[1;5;${c_danger}m▲\e[0m"
result="\e[1;${c_danger}m$certLineTime\e[0m"
fi
else
domain="\e[${c_txt}m${domain}\e[0m"
sign="\e[1;2;${c_warning}m\U25B2\e[0m"
result="\e[1;2;${c_warning}mtimeout or misnamed\e[0m"
fi
elif [[ $INTERNET_OUT == 1 ]]; then
domain="\e[${c_txt}m${domain}\e[0m"
sign="\e[1;2;${c_txt}m▲\e[0m"
result="\e[1;2;${c_txt}mno internet, skipped\e[0m"
fi
output+="\n ${sign} ${domain}|${port}|$result"
done
# output header and table content
echo -e "\e[1A\e[${c_title} TLS checks \e[0m "
echo -e
echo -e "$output" | column -t -s '|'

74
motdfetch.d/05-postqueue Normal file
View file

@ -0,0 +1,74 @@
#!/usr/bin/env bash
#
# check if there is some mails in the deferred queue of postfix
# + danger color if >= 100 messages
# + warning color if >= 1 messages
#
# requirements: sudo apt install postfix
# GENERAL ###########################################################
# locale env
unset LC_ALL
export LC_MESSAGES=C
# check if module was disabled
module_disable=${module_postqueue_disable:=0}
if (($module_disable == 1)); then
exit 1
fi
# colors
c_txt=${c_txt:="39"}
c_txt_emphase=${c_txt_emphase:="35"}
c_txt_deco=${c_txt_deco:="97"}
c_txt_invert=${c_txt_invert:="30"}
c_bg=${c_bg_sec:="47"}
c_danger=${c_danger:="31"}
c_warning=${c_warning:="33"}
c_success=${c_success:="32"}
c_title=${c_title:="${c_bg};1;${c_txt_invert}m"}
# PREPARATIONS ######################################################
# print loading message
echo -e "\nWaiting for Postqueue checks\e[5m...\e[0m"
# check if postfix is available, with qshape
if ! command -v qshape 1>/dev/null; then
# output module header
echo -e "\e[1A\e[${c_title} Postfix deferred queue status \e[0m\n"
echo -e " no Postfix server available"
exit 1
fi
# parse deferred messages qty with qshape
deferredcmd=$(timeout 5 qshape -b 6 deferred 2>/dev/null)
# OUTPUT ############################################################
# if qshape timeout 5sec
if [[ "$deferredcmd" == "" ]]; then
# output module header
echo -e "\e[1A\e[${c_title} Postfix deferred \e[0m\n"
echo -e " \e[1;${c_warning}mqshape needs more than 5 sec to parse the deferred Mails list,\n you should investigate!\e[0m"
exit 1
fi
# if total deferred is 0
deferredtotal=$(echo -e "$deferredcmd" | grep TOTAL | awk '{print $2}')
if [[ "$deferredtotal" == "0" ]]; then
# output module header
echo -e "\e[1A\e[${c_title} Postfix deferred queue status \e[0m\n"
echo -e " There is \e[1;${c_success}m${deferredtotal}\e[0m deferred Mails."
exit 1
fi
# else print qshape output
# output module header
echo -e "\e[1A\e[${c_title} Postfix deferred queue status \e[0m\n"
if [[ "$deferredtotal" -gt 100 ]]; then
echo -e " There is \e[1;${c_danger}m${deferredtotal}\e[0m deferred Mails in queue."
else
echo -e " There is \e[1;${c_warning}m${deferredtotal}\e[0m deferred Mails in queue."
fi

77
motdfetch.d/06-fail2ban Normal file
View file

@ -0,0 +1,77 @@
#!/usr/bin/env bash
#
# parse Fail2ban jail status, display failed and banned counts
# + Warning if currently more than 0
# + Danger if currently more than 20
#
# /!\ need root to display status, display warning for other user
#
# requirements: sudo apt install fail2ban
# GENERAL ###########################################################
# locale env
unset LC_ALL
export LC_MESSAGES=C
# check if module was disabled
module_disable=${module_fail2ban_disable:=0}
if (($module_disable == 1)); then
exit 1
fi
# colors
c_txt=${c_txt:="39"}
c_txt_emphase=${c_txt_emphase:="35"}
c_txt_deco=${c_txt_deco:="97"}
c_txt_invert=${c_txt_invert:="30"}
c_bg=${c_bg_sec:="47"}
c_danger=${c_danger:="31"}
c_warning=${c_warning:="33"}
c_success=${c_success:="32"}
c_title=${c_title:="${c_bg};1;${c_txt_invert}m"}
# PREPARATIONS ######################################################
# check if fail2ban is available
if ! command -v fail2ban-client 1>/dev/null; then
# output module header
echo -e "\n\e[${c_title} Fail2Ban status \e[0m\n"
echo -e " no Fail2Ban server available"
exit 1
elif [[ ! `id -un` == "root" ]]; then
echo -e "\n\e[${c_title} Fail2Ban status \e[0m\n"
echo -e " you must be root to get Fail2Ban status"
exit 1
fi
# OUTPUT ############################################################
echo -e "\n\e[${c_title} Fail2Ban status \e[0m\n"
# fail2ban-client status to get all jails, takes about ~70ms
jails=($(fail2ban-client status | grep "Jail list:" | sed "s/ //g" | awk '{split($2,a,",");for(i in a) print a[i]}'))
out="\e[1;4;${c_txt}mJail name\e[24m,\e[4mFailed\e[24m,\e[4mTotal\e[24m,\e[4mBanned\e[24m,\e[4mTotal\e[24m\n"
for jail in ${jails[@]}; do
# slow because fail2ban-client has to be called for every jail (~70ms per jail)
status=$(fail2ban-client status ${jail})
failed=$(echo "$status" | grep -ioP '(?<=Currently failed:\t)[[:digit:]]+')
if [[ $failed -ge 20 ]]; then
failed="\e[1;5;${c_danger}m${failed}\e[0m"
elif [[ $failed -ge 1 ]]; then
failed="\e[1;${c_warning}m${failed}\e[0m"
fi
totalfailed=$(echo "$status" | grep -ioP '(?<=Total failed:\t)[[:digit:]]+')
banned=$(echo "$status" | grep -ioP '(?<=Currently banned:\t)[[:digit:]]+')
if [[ $banned -ge 20 ]]; then
banned="\e[1;5;${c_danger}m${banned}\e[0m"
elif [[ $banned -ge 1 ]]; then
banned="\e[1;${c_warning}m${banned}\e[0m"
fi
totalbanned=$(echo "$status" | grep -ioP '(?<=Total banned:\t)[[:digit:]]+')
out+="\e[1;${c_txt_emphase}m${jail}\e[0m,$failed,$totalfailed,$banned,$totalbanned\n"
done
echo -e "$out" | column -ts $',' | sed 's/^/ /'

77
motdfetch.d/99-userslog Normal file
View file

@ -0,0 +1,77 @@
#!/usr/bin/env bash
#
# check last user logged in (with host ip and period) and currently logged in users
# + if the last logged in is not the current user, add a section about the last login of the curent user
# GENERAL ###########################################################
# locale env
unset LC_ALL
export LC_MESSAGES=C
# check if module was disabled
module_disable=${module_userslog_disable:=0}
if (($module_disable == 1)); then
exit 1
fi
# colors
c_txt=${c_txt:="39"}
c_txt_emphase=${c_txt_emphase:="35"}
c_txt_deco=${c_txt_deco:="97"}
c_txt_invert=${c_txt_invert:="30"}
c_bg=${c_bg_sec:="47"}
c_danger=${c_danger:="31"}
c_warning=${c_warning:="33"}
c_success=${c_success:="32"}
c_title=${c_title:="${c_bg};1;${c_txt_invert}m"}
# PREPARATIONS ######################################################
lastuserlog=$(last -i -F -n 2 | head -2 | tail -1)
lastusername=$(echo -e "$lastuserlog" | awk '{print $1}')
lastuserhost=$(echo -e "$lastuserlog" | awk '{print $3}')
if [[ "$lastuserhost" == "0.0.0.0" ]]; then
lastuserhost="localhost"
fi
lastusertime=$(echo -e "$lastuserlog" | awk '{$1=$2=$3=""; print $0}' | sed 's/^ //')
lastcurrentuserlog=$(last $(id -un) -i -F -n 2 | head -2 | tail -1)
lastcurrentusername=$(echo -e "$lastcurrentuserlog" | awk '{print $1}')
lastcurrentuserhost=$(echo -e "$lastcurrentuserlog" | awk '{print $3}')
if [[ "$lastcurrentuserhost" == "0.0.0.0" ]]; then
lastcurrentuserhost="localhost"
fi
lastcurrentusertime=$(echo -e "$lastcurrentuserlog" | awk '{$1=$2=$3=""; print $0}' | sed 's/^ //')
loggedusers=$(last -i -F -p now | /usr/bin/grep "logged in" | awk '{print $1}')
loggedusersnames=$(echo -e "$loggedusers" | uniq -c | sed 's/^\s\+/|/' | tr ' ' '|')
loggeduserscount=$(echo -e "$loggedusers" | wc -l)
# OUTPUT ############################################################
# header
echo -e "\n\e[${c_title} Users log \e[0m\n"
W="\e[0;${c_txt}m"
X="\e[1;${c_txt}m"
F="\e[0;${c_txt_emphase}m"
G="\e[1;${c_txt_emphase}m"
S="\e[1;4;${c_txt}m"
N="\e[0m"
# last logins
if [[ "$lastusername" == "$lastcurrentusername" ]]; then
echo -e "$X Last user logged in$W: $G$lastcurrentusername$W, from $F$lastcurrentuserhost$W
$X | Login - out$W (duration): $F$lastcurrentusertime$N"
else
echo -e "$X Last user logged in$W: $G$lastusername$W, from $F$lastuserhost$W
$X | Login - out$W (duration): $F$lastusertime$W
$X Last time$W $G$lastcurrentusername$W$X logged in$W, was from $F$lastcurrentuserhost$W
$X | Login - out$W (duration): $F$lastcurrentusertime$N"
fi
# logged users
echo
echo -e "$G"$loggeduserscount"$W ${X}session$W(s)$X are logged in$W:|${S}Session(s)${W}|${S}User$W${G}\n`echo -e "$loggedusersnames"`$N" | column -t -s '|' | sed 's/^/ /'

70
motdfetch.sample.conf Normal file
View file

@ -0,0 +1,70 @@
#!/usr/bin/env bash
#
# user configuration file for MOTDfetch
# GLOBAL COLORS ######################################################
c_txt="39"
c_txt_emphase="35"
c_txt_deco="97"
c_txt_invert="30"
c_bg="47"
c_danger="31"
c_warning="33"
c_success="32"
c_title="${c_bg};1;${c_txt_invert}m"
# GLOBAL OPTIONS ####################################################
## api website name for plain public IP4/6 check
global_publicip_api="icanhazip.com"
# MODULES OPTIONS ####################################################
## HEADER module
module_header_disable=0
### custom text (comment to use username, trim > 80 char)
module_header_text="BANNER"
## SYSINFO module
module_sysinfo_disable=0
## SERVICES module
module_services_disable=0
### services to check
### + lookup system & current user services running with these 2 commands:
### systemctl --type=service --no-pager -q --state running
### systemctl --type=service --user --no-pager -q --state running
### + to add user services you need to provide the uid of the user ("service:uid"), find the uid with:
### id -u
module_services=(
"cron"
"unattended-upgrades.service"
"dbus:1000"
)
### column max-width
module_services_width="80"
## FILESYSTEM module
module_filesystem_disable=0
### disable the ZFS section of the module
module_filesystem_zfs_disable=0
### max blocks usage percentage warning threshold
module_filesystem_max_usage="85"
## TLS module
module_tls_disable=0
### Domains and ports (default:433) for TLS certification check
module_tls_domains=(
"www.google.com"
"w.fakedomain.com"
)
## POSTQUEUE module
module_postqueue_disable=0
## FAIL2BAN module
module_fail2ban_disable=0
## USERLOG module
module_userslog_disable=0

112
readme.md Normal file
View file

@ -0,0 +1,112 @@
# [MOTDfetch][motdfetch_repo]
![Bash](https://img.shields.io/badge/bash-1f425f.svg?style=for-the-badge&logo=image%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw%2FeHBhY2tldCBiZWdpbj0i77u%2FIiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8%2BIDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkE3MDg2QTAyQUZCMzExRTVBMkQxRDMzMkJDMUQ4RDk3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkE3MDg2QTAzQUZCMzExRTVBMkQxRDMzMkJDMUQ4RDk3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTcwODZBMDBBRkIzMTFFNUEyRDFEMzMyQkMxRDhEOTciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTcwODZBMDFBRkIzMTFFNUEyRDFEMzMyQkMxRDhEOTciLz4gPC9yZGY6RGVzY3JpcHRpb24%2BIDwvcmRmOlJERj4gPC94OnhtcG1ldGE%2BIDw%2FeHBhY2tldCBlbmQ9InIiPz6lm45hAAADkklEQVR42qyVa0yTVxzGn7d9Wy03MS2ii8s%2BeokYNQSVhCzOjXZOFNF4jx%2BMRmPUMEUEqVG36jo2thizLSQSMd4N8ZoQ8RKjJtooaCpK6ZoCtRXKpRempbTv5ey83bhkAUphz8fznvP8znn%2B%2F3NeEEJgNBoRRSmz0ub%2FfuxEacBg%2FDmYtiCjgo5NG2mBXq%2BH5I1ogMRk9Zbd%2BQU2e1ML6VPLOyf5tvBQ8yT1lG10imxsABm7SLs898GTpyYynEzP60hO3trHDKvMigUwdeaceacqzp7nOI4n0SSIIjl36ao4Z356OV07fSQAk6xJ3XGg%2BLCr1d1OYlVHp4eUHPnerU79ZA%2F1kuv1JQMAg%2BE4O2P23EumF3VkvHprsZKMzKwbRUXFEyTvSIEmTVbrysp%2BWr8wfQHGK6WChVa3bKUmdWou%2BjpArdGkzZ41c1zG%2Fu5uGH4swzd561F%2BuhIT4%2BLnSuPsv9%2BJKIpjNr9dXYOyk7%2FBZrcjIT4eCnoKgedJP4BEqhG77E3NKP31FO7cfQA5K0dSYuLgz2TwCWJSOBzG6crzKK%2BohNfni%2Bx6OMUMMNe%2Fgf7ocbw0v0acKg6J8Ql0q%2BT%2FAXR5PNi5dz9c71upuQqCKFAD%2BYhrZLEAmpodaHO3Qy6TI3NhBpbrshGtOWKOSMYwYGQM8nJzoFJNxP2HjyIQho4PewK6hBktoDcUwtIln4PjOWzflQ%2Be5yl0yCCYgYikTclGlxadio%2BBQCSiW1UXoVGrKYwH4RgMrjU1HAB4vR6LzWYfFUCKxfS8Ftk5qxHoCUQAUkRJaSEokkV6Y%2F%2BJUOC4hn6A39NVXVBYeNP8piH6HeA4fPbpdBQV5KOx0QaL1YppX3Jgk0TwH2Vg6S3u%2BdB91%2B%2FpuNYPYFl5uP5V7ZqvsrX7jxqMXR6ff3gCQSTzFI0a1TX3wIs8ul%2Bq4HuWAAiM39vhOuR1O1fQ2gT%2F26Z8Z5vrl2OHi9OXZn995nLV9aFfS6UC9JeJPfuK0NBohWpCHMSAAsFe74WWP%2BvT25wtP9Bpob6uGqqyDnOtaeumjRu%2ByFu36VntK%2FPA5umTJeUtPWZSU9BCgud661odVp3DZtkc7AnYR33RRC708PrVi1larW7XwZIjLnd7R6SgSqWSNjU1B3F72pz5TZbXmX5vV81Yb7Lg7XT%2FUXriu8XLVqw6c6XqWnBKiiYU%2BMt3wWF7u7i91XlSEITwSAZ%2FCzAAHsJVbwXYFFEAAAAASUVORK5CYII%3D)
![Debian](https://img.shields.io/badge/Debian-D70A53?style=for-the-badge&logo=debian&logoColor=white)
![Ubuntu](https://img.shields.io/badge/Ubuntu-E95420?style=for-the-badge&logo=ubuntu&logoColor=white)
**MOTDfetch** is a modular & dynamic **MOTD replacement** written in Bash and a nice **command line information tool** for Debian/Ubuntu systems.
>*MOTD - "message of the day"
>Feature used on Unix-like CLI systems to send a common message to all users after a successful login, and just before it executes the login shell.
>Newer Unix-like systems may generate the message dynamically when the host boots or a user logs in.*
**Drawbacks** with the current implementation of **MOTD with PAM on Debian and Ubuntu** are at least 3:
1. The **SSH client does not decide** whether to activate the MOTD.
As it is loaded before the shell login is opened:
2. User has to **wait blindly** for the end of the execution if there is a dynamic script.
3. Scripts **can not check the user sessions** status.
MOTDfetch **avoids** and partially **disable the original MOTD implementation**, but **remains compatible** with it for PAM login.
---
>###### Table of contents
>**[FEATURES](#features)**
>**[INSTALLATION](#installation)**
>**[USAGE](#usage)**
[![Preview of MOTDfetch][preview_image]][preview_image_url]
---
## Features
- Usable as a **command**, a **login MOTD** or **SSH MOTD**.
- **[8 Core modules](#8_core_modules)**.
- Uniform and **easily customizable**.
- **Emphasis** of important elements and status by **colors, symbols or flashing**.
- **Notice message** upon missing dependencies
- **Reduced operation mode fot high load** (enforceable).
- Separate **system and users configuration** files.
- **Scripted installation** and updates.
- **Modules or sections can be disabled**.
- Standalone **executable modules**.
### 8 Core modules
#### **HEADER module**
- decorative banner (custom text or username)
- welcome message with username, date and time
- user mail inbox status
#### **SYSYINFO module**
- distribution name, kernel version, uptime
- CPU, cores, load, processes, memory, swap
- hostname, network interface, DNS servers, public IPs/DNS/rDNS records
- custom external API for public IP check
- skip connectivity related tests if the API check fail
- short timeout on dig & curl commands to keep the script snappy
#### **SERVICES module**
- check systemd services status
- by service name and user UID if not system
- :warning: user cannot check other user's services
#### **FILESYSTEM module**
- 2 sections: general filesystems, ZFS filesystems
- name, fs type, usage and size of disk or ZFS pool
- inodes usage and size when relevant
- health status for ZFS
- custom usage threshold for warning hints
#### **TLS module**
- check X.509 certificates status with openssl
- accept domain and optional port (443 by default)
- validate name correspondence and expiration date
- skip the test if there is no internet connectivity
- short timeout on openssl command to keep the script snappy
#### **POSTQUEUE module**
- postfix deferred queue status
- short timeout on the parsing to keep the script snappy if there is thousands of mails
#### **FAIL2BAN module**
- display fail2ban jails status
- :warning: need to be executed with root or sudo
#### **USERSLOG module**
- display last user logged in
- name, IP and connection range
- if the current user is different, display his last log too
- display currently logged sessions with usernames
## Installation
### Requirements
## Usage
---
[//]: # (LINKS)
[motdfetch_repo]: https://git.tkapias.net/tkapias/MOTDfetch
[preview_image]: docs/img/preview.jpg "Preview of MOTDfetch"
[preview_image_url]: https://git.tkapias.net/tkapias/MOTDfetch/raw/master/docs/img/preview.jpg