Following on from
bashrc
, here is a snapshot and explaination of my .motd
function prx {
local rand=$( tr -dc 'a-fA-F0-9' < /dev/urandom | head -c6 )
printf "\r$(command -v bashColor >/dev/null && bashColor sf $rand)"
printf "~\e[0m"
}
Fun colours first, using the bashColor
function from my .bashrc we pull random alphanumeric characters within the hexidecimal range and feed that to bashColor
to return a terminal color escape sequence. The last line prints the preferred message prefix and the reset-formatting escape code.
function print_iostat {
while read -r line; do
[[ ! "$line" =~ ^(s|v)d ]] && continue
local col1="$(printf "$line" | awk '{print $1}')"
local col2="$(printf "$line" | awk '{print $2}')"
local col3="$(printf "$line" | awk '{print $3}')"
local col4="$(printf "$line" | awk '{print $4}')"
[[ "$col2" == "0.00" ]] && continue
local rust="$(cat /sys/block/$col1/queue/rotational)"
[[ $rust == 0 ]] && local is_rust="SSD" || local is_rust="spinning disk"
printf "\r~ /dev/$col1 seeing ${col3}MiB w/sec and ${col4}MiB r/sec in $col2 IOPS on ${is_rust}\n"
done <<< "$( iostat -d -m | sed '/^$/d' | tail -n+3 | awk '{print $1,$2,$3,$4}' )"
}
print_iostat
outputs something like:
~ /dev/sda seeing 0.13MiB w/sec and 0.01MiB r/sec in 7.20 IOPS on spinning disk
Starting with the last line we run iostat
and trim it down to just the data we are interested in; using sed
to purge the empty lines, tail
to remove the top three lines of headers and awk
to return specific columns.
Now we can iterate over these lines, each line covering a different drive. We drop anything which doesn’t match the ^(s|v)d
pattern, this limits our inputs to just physical drives, excluding things like systemd user slices.
After assigning each column to variable for brevity later, we then check if the IOPS is zero and skip that record.
We find out whether the disk being reported on is a rotating mechanical disk or a solid state drive with /sys/block/sda/queue/rotational
, giving 1=rotational
and 0=ssd
.
Everything below is in the function print_motd
# Trap Ctrl-C to kill motd
trap return INT
It’s important to be able abort .motd
’s execution, namely with ^C
. By trapping the Interrupt signal we can trigger a function return instead of the usual ^C
behaviour being caught by bash and ending the session before it’s begun.
local uptime="$( uptime | tr -s ' ' | awk -F, '{ print $1 }' | cut -d\ -f4- )"
local uptime="$( printf "${uptime}" | awk -F\: '{print $1, "hours", $2, "minutes"}' )"
printf "\r~ $(hostname -s) up for ${uptime}\n"
local loadavg="$( uptime | sed 's/^.*age: //' )"
printf "\r~ System load is ${loadavg}\n"
Humanify uptime
’s output into hostname up for x hours y minutes
and System load is 0.06, 0.08, 0.08
.
if [ "$bashrc_end" -gt 1 ]; then
print_iostat
fi
I’ve configured print_iostat
to only print when bash takes more than a second to load, as I know my bashrc takes much less than a second to execute; the most likely reason for slow start-up is the system disk is busy, which is good to know upon logging in.
usrcount="$( /usr/bin/w | tail -n+3 | awk '{print $1}' | uniq | wc -l )"
printf "\r~ Logged in as $USER - "
if [[ "${usrcount}" == "1" ]]; then
printf "You're the only one here!"
else
printf "${usrcount} users online."
fi
This section humanifies w
’s output, as-well as indicating which user you logged in as. The usrcount
variable stores the count of a deduplcated list of users which is printed if the count is greater than one, otherwise it states how lonely the box feels.
recent_usr=$(last | head -n-2 | grep "$(date '+%a %b %d')" | egrep -v "$USER|reboot|still logged in" | awk '{print $1}' | sort | uniq | wc -l)
past_usr=$(expr $recent_usr - $usrcount | tr -d '-')
if [[ "$past_usr" -gt 1 ]]; then
[[ "${usrcount}" == "1" ]] && printf " but" || printf " with"
printf " $past_usr have been on today.\n"
else
printf "\n"
fi
Here I count the users who have logged in today; this indicator could be useful when investigating boxes which shouldn’t typically have any logins for example. It seems on popular boxes /var/log/last
takes long enough to read that it’s noticable.
This was a nice idea which ended up being too heavy to leave in by default, but I like to keep it around. I could start loading this file further up the script and throw it into the background, ready to be read at this stage - but I haven’t done this.
usrps=$(ps aux | tail -n+2)
while read -r line; do
printf '$line\n' | awk '$8 ~ /Z/ {print $1 " has zombie " $2}'
done <<< "$usrps"
Another questionably useful section, reading ps aux
line by line to find Zombie processes, these can tie up a box’s resources, cause other processes to fail and eventually cause a crash. So it’s important to know if one exists - I haven’t had a chance to test this and I suspect it will fail because ps aux
will hang once it encounters the zombie process… #FIXME
if [[ "$bashrc_end" ]] && [[ "$bashrc_end" -gt 1 ]]; then
printf "\r~ bash started in ${bashrc_end} second"
case "${bashrc_end}" in
"1") printf "!\n";;
*) printf "s!\n";;
esac
fi
Lastly I want to know if bashrc started slowly, this variable is already used by print_iostat
but I like to have a specific message too. I use a simple case
to make make “second” plural based on the number of seconds.