/etc/profile.d/loginnotify.sh
#!/bin/sh
###############################################################################
# loginnotify.sh
#
# SSH login/logout notification via ntfy
#
# Install:
# /etc/profile.d/loginnotify.sh
###############################################################################
###############################################################################
# Configuration
###############################################################################
# Prevent multiple sourcing
if [ -n "${LOGINNOTIFY_LOADED:-}" ]; then
return 0
fi
readonly LOGINNOTIFY_LOADED=1
readonly NTFY_BIN="/usr/bin/ntfy"
readonly IP_BIN="$HOME/Projects/Mine/WLB/tools/ip2region/ip2region"
readonly TMP_DIR="/tmp"
readonly LOG_FILE="${TMP_DIR}/loginnotify.log"
readonly NTFY_TOPIC="loginnotify"
readonly TITLE_LOGIN="SSH Login"
readonly TITLE_LOGOUT="SSH Logout"
readonly HOST="${HOSTNAME:-$(hostname)}"
readonly JQ_BIN="$(command -v jq 2>/dev/null || true)"
# Only handle SSH sessions.
[ -n "$SSH_CONNECTION" ] || return 0
###############################################################################
# Helper Functions
###############################################################################
_get_tty() {
basename "${SSH_TTY:-$(tty 2>/dev/null)}" | tr '/' '-'
}
_get_client_ip() {
local ip
ip="${SSH_CONNECTION%% *}"
printf '%s\n' "${ip:-localhost}"
}
_get_location() {
local ip="$1"
local result
local region
case "$ip" in
""|localhost)
printf '%s\n' "localhost"
return
;;
esac
if [ ! -x "$IP_BIN" ] || [ ! -x "$JQ_BIN" ]; then
printf '%s\n' "unknown"
return
fi
if ! result=$("$IP_BIN" "$ip" 2>/dev/null); then
printf '%s\n' "unknown"
return
fi
region=$(
"$JQ_BIN" -r '.region // empty' <<<"$result" 2>/dev/null
)
if [[ "$region" == Reserved* ]]; then
printf '%s\n' "private"
elif [ -z "$region" ]; then
printf '%s\n' "unknown"
else
printf '%s\n' "$region"
fi
}
_collect_info() {
local client_ip location tty_dev
client_ip=$(_get_client_ip)
location=$(_get_location "$client_ip")
tty_dev=$(_get_tty)
printf '%s\n' \
"Client: $client_ip
Location: $location
TTY: $tty_dev"
}
_get_timefile() {
printf '%s/loginnotify_%s_%s\n' \
"$TMP_DIR" \
"$USER" \
"$(_get_tty)"
}
_format_duration() {
local seconds="$1"
local days
local hours
local mins
days=$((seconds / 86400))
hours=$(((seconds % 86400) / 3600))
mins=$(((seconds % 3600) / 60))
if (( days > 0 )); then
printf "%dd %dh %dm" "$days" "$hours" "$mins"
elif (( hours > 0 )); then
printf "%dh %dm" "$hours" "$mins"
else
printf "%dm" "$mins"
fi
}
_send_ntfy_async() {
local title="$1"
local message="$2"
[ -x "$NTFY_BIN" ] || return
nohup "$NTFY_BIN" publish --title "$title $USER@$HOST" "$NTFY_TOPIC" "$message" >>"$LOG_FILE" 2>&1 </dev/null &
disown
}
###############################################################################
# Business Logic
###############################################################################
loginnotify_login() {
date +%s > "$(_get_timefile)"
_send_ntfy_async \
"$TITLE_LOGIN" \
"$(_collect_info)"
}
loginnotify_logout() {
local timefile
local duration_str="unknown"
local login_time
local logout_time
local duration
timefile=$(_get_timefile)
if [ -f "$timefile" ]; then
read -r login_time < "$timefile"
logout_time=$(date +%s)
duration=$((logout_time - login_time))
duration_str=$(_format_duration "$duration")
rm -f "$timefile"
fi
_send_ntfy_async \
"$TITLE_LOGOUT" \
"$(_collect_info)
Duration: $duration_str"
}
###############################################################################
# Main (bash only)
###############################################################################
if [ -n "$BASH_VERSION" ]; then
if [ -n "$SSH_CONNECTION" ]; then
trap loginnotify_logout EXIT
loginnotify_login
fi
fi
/etc/zsh/zlogin
# /etc/zsh/zlogin: system-wide .zlogin file for zsh(1).
#
# This file is sourced only for login shells. It
# should contain commands that should be executed only
# in login shells. It should be used to set the terminal
# type and run a series of external commands (fortune,
# msgs, from, etc.)
#
# Global Order: zshenv, zprofile, zshrc, zlogin
if [ -r /etc/profile.d/loginnotify.sh ]; then
source /etc/profile.d/loginnotify.sh
fi
if [[ -n "$SSH_CONNECTION" ]]; then
loginnotify_login
fi
/etc/zsh/zlogout
# /etc/zsh/zlogout: system-wide .zlogout file for zsh(1).
if [ -r /etc/profile.d/loginnotify.sh ]; then
source /etc/profile.d/loginnotify.sh
fi
if [[ -n "$SSH_CONNECTION" ]]; then
loginnotify_logout
fi