#!/bin/bash
# NAME: ssh-activity
# PATH: /mnt/e/bin
# DESC: When ssh client types in terminal send dbus method for user activity.
# CALL: Must run on host. Shaould be called in startup applications after user
# logs into host desktop. Broadcast message when inactive that system
# going down in 60, 30, 15, 10, 5, 3, 2, 1, 0 minute(s).
# PARM: --n (-no-blank-lock) and -d (--debug)
# DATE: June 21, 2020. Modified: Original Version.
# NOTE: After updating run: scp /mnt/e/bin/ssh-activity dell:/mnt/e/bin
# For Debugging:
# on host run: ssh-activity -d | tee ssh-activity.log
# on client run: while : ; do echo "========== ssh-activity.log $(date) ==========" ; tail ssh-activity.log ; sleep 60 ; done
# Global constants
export LANG=C # Force english names for sed & grep searches
SLEEP_SECS=60 # Seconds to sleep between 'w -ish' command usage
REMOTE="192.168.0" # What we 'grep' for in 'w -ish' output
fDebug=false # When debug is on issue progress messages
fNoBlankLock=false # Don't blank or lock screen
ParseParms () {
while [[ $# -gt 0 ]] ; do
key="$1"
case $key in
-d|--debug)
fDebug=true
echo "Development Mode = $fDebug"
shift # past argument
;;
-n|--no-blank-lock)
fNoBlankLock=true
shift # past argument
;;
*) # unknown option
echo "Usage: ssh-activity -d (--debug) -n (--no-blank-lock)"
exit
;;
esac
done
} # ParseParms
# Must have the xprintidle package.
command -v xprintidle >/dev/null 2>&1 || { echo >&2 \
"'xprintidle' package required but it is not installed. Aborting."; \
exit 3; }
# Global gsettings set by Init () function need to control screen & suspending
GsAcTimeOut=0 # idle seconds until system sleeps (0=never for all settings)
GsIdleDelay=0 # idle seconds until screen blanks (a good thing for server)
GsLockDelay=0 # idle seconds until screen locks (a bad thing if logged in)
GsLockEnabled=0 # is lock screen enabled? ('false' overrides GsLockDelay)
fOneTime=false # for displaying debugging information one time only
Init () {
# gsettings required to know when system shuts down due to xidle time
GsAcTimeOut=$(gsettings get org.gnome.settings-daemon.plugins.power \
sleep-inactive-ac-timeout)
# gsettings required for blanking and locking screen when no host activity
if [[ $fNoBlankLock == false ]] ; then
GsIdleDelay=$(gsettings get org.gnome.desktop.session idle-delay)
# Cut out rightside value from 'uint32 0'
GsIdleDelay="${GsIdleDelay##* }"
[[ $GsIdleDelay -eq 0 ]] && GsIdleDelay=999999
GsLockDelay=$(gsettings get org.gnome.desktop.screensaver lock-delay)
GsLockDelay="${GsLockDelay##* }"
[[ $GsLockDelay -eq 0 ]] && GsLockDelay=999999
GsLockEnabled=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
fi
if [[ $fDebug == true && $fOneTime == false ]] ; then
echo "$0 started at: $(date)"
echo "GsAcTimeOut: $GsAcTimeOut"
echo "GsIdleDelay: $GsIdleDelay"
echo "GsLockDelay: $GsLockDelay"
echo "GsLockEnabled: $GsLockEnabled"
fOneTime=true
fi
} # Init
GetWish () {
# 'w' command '-ish' arguments (--ip-adddr, --short, --no-header) returns:
# rick pts/21 192.168.0.12 4.00s sshd: rick [priv]
local ArrEntCnt ArrCols ArrRows CheckSum i
ArrCols=5
WishArr=( $(w -ish | grep "$REMOTE" | tr -s " " | \
cut -d' ' -f1-"$ArrCols") )
# The fifth column on each row repurposed to be idle time seconds
ArrEntCnt="${#WishArr[@]}"
[[ $ArrEntCnt -lt "$ArrCols" ]] && return 1 # No remote users
ArrRows=$(( $ArrEntCnt / ArrCols ))
CheckSum=$(( ArrRows * ArrCols ))
# Error possible if 'w -ish' command breaks down
[[ $ArrEntCnt -ne "$CheckSum" ]] && { echo CheckSum failed ; \
return 2 ; }
LowestSeconds=999999
for (( i=0; i<ArrEntCnt; i=i+ArrCols )) ; do
# Time formatted as DDdays, HH:MMm, MM:SSs & SS.CC convert to Seconds
WishSeconds "${WishArr[i+3]}" Seconds
WishArr[i+4]=$Seconds # Store in repurposed array column # 5
[[ $Seconds -lt "$LowestSeconds" ]] && LowestSeconds="$Seconds"
[[ $fDebug == true ]] && echo "${WishArr[i]} ${WishArr[i+1]} \
${WishArr[i+2]} Wish Time: ${WishArr[i+3]} Seconds: ${WishArr[i+4]} "
done
return 0
} # GetWish
: <<'END'
/* ------------ NOTES --------------------------------------------------------
$ w -ish
rick tty7 :0 2days /sbin/upstart --user
rick pts/21 192.168.0.12 4.00s sshd: rick [priv]
AND THEN LATER ON....
rick pts/21 192.168.0.12 44.00s sshd: rick [priv]
rick pts/21 192.168.0.12 1:24 sshd: rick [priv]
rick pts/21 192.168.0.12 2:04 sshd: rick [priv]
From: https://serverfault.com/questions/302455/
how-to-read-the-idle-column-in-the-output-of-the-linux-w-command/
302462#302462
From the man page:
The standard format is DDdays, HH:MMm, MM:SS or SS.CCs .
if the times are greater than 2 days, 1hour, or 1 minute respectively.
so your output is MM:SS (>1m and <1 hour).
---------------------------------------------------------------------------- */
END
WishSeconds () {
# PARM 1: 'w -ish' command idle time 44.00s, 5:10, 1:28m, 3days, etc.
# 2: Variable name (no $ is used) to receive idle time in seconds
# NOTE: Idle time resets to zero when user types something in terminal.
# A looping job calling a command doesn't reset idle time.
local Wish Unit1 Unit2
Wish="$1"
declare -n Seconds=$2
# Leading 0 is considered octal value in bash. Change ':09' to ':9'
Wish="${Wish/:0/:}"
if [[ "$Wish" == *"days"* ]] ; then
Unit1="${Wish%%days*}"
Seconds=$(( Unit1 * 86400 ))
elif [[ "$Wish" == *"m"* ]] ; then
Unit1="${Wish%%m*}"
Unit2="${Unit1##*:}"
Unit1="${Unit1%%:*}"
Seconds=$(( (Unit1 * 3600) + (Unit2 * 60) ))
elif [[ "$Wish" == *"s"* ]] ; then
Seconds="${Wish%%.*}"
else
Unit1="${Wish%%:*}"
Unit2="${Wish##*:}"
Seconds=$(( (Unit1 * 60) + Unit2 ))
fi
} # WishSeconds
HostShutDownMessage () {
# Send wall message 60, 30, 15, 10, 5, 3, 2 and 1 minute(s) before shutdown
[[ $GsAcTimeOut == 0 ]] && return # System never shuts down
MinutesLeft=$(( ( GsAcTimeOut / 60 ) - ( IdleSeconds / 60 ) ))
case $MinutesLeft in
60|30|15|10|5|3|2|1)
[[ $fDebug == true ]] && \
echo "'wall' broadcast: shutdown in: $MinutesLeft minute(s)."
wall "If no activity, shutdown in: $MinutesLeft minute(s)." ;;
0)
[[ $fDebug == true ]] && \
echo "Host system shutdown at: $(date)"
wall "HOST SYSTEM SHUTDOWN at: $(date)" ;;
esac
} # HostShutDownMessage
SSC_Result=""
ScreenSaverCommand () {
# Send dbus method to screen saver
Parm1="$1" # GetActiveTime, Inhibit, Throttle, Lock, UnThrottle, Unihibit,
# GetActive, SetActive (requires true), SimulateUserActivity,
# GetSessionIdleTime (broken!)
Parm2="$2" # Optional, a value like 'true'
# If parameter 2 not passed force it to be unset, instead of null parm
SSC_Result=$(gdbus call --session --dest org.gnome.ScreenSaver \
--object-path /org/gnome/ScreenSaver \
--method org.gnome.ScreenSaver."$Parm1" ${Parm2:+"$Parm2"})
[[ $fDebug == true ]] && echo Screen Saver Command: $Parm1 $Parm2 \
Result: $SSC_Result
} # ScreenSaverCommand
CheckToBlankOrLock () {
# Comments from above
# GsAcTimeOut=0 # idle seconds until system sleeps (0=never for all settings)
# GsIdleDelay=0 # idle seconds until screen blanks (a good thing for server)
# GsLockDelay=0 # idle seconds until screen locks (a bad thing if logged in)
# GsLockEnabled=0 # is lock screen enabled? ('false' overrides GsLockDelay)
[[ $fDebug == true ]] && echo IdleSeconds: $IdleSeconds \
ResetIdleSeconds: $ResetIdleSeconds
# Is screen blanked ?
ScreenSaverCommand "GetActive"
if [[ $SSC_Result == *"false"* ]] ; then
# Screen is not blanked. We may have to blank it though
ScreenSaverCommand "SimulateUserActivity"
NewIdle="$(xprintidle)"
[[ $NewIdle -gt 1000 ]] && echo "ERROR: Idle time not reset: $NewIdle"
# Screen is not blanked, check if we should blank it
if [[ $ResetIdleSeconds -gt $GsIdleDelay ]] ; then
[[ $fDebug == true ]] && echo Forcing screen blank
ScreenSaverCommand "SetActive" true
else
: # We didn't blank screen but it is blank now from host inactivity
# No need to simulate activity while user is active
fi
else
: # Screen is blanked before simulation, blank it afterwards
# [[ $fDebug == true ]] && echo Simiulating activity and blanking screen
ScreenSaverCommand "SimulateUserActivity"
ScreenSaverCommand "SetActive" true
NewIdle="$(xprintidle)"
[[ $NewIdle -gt 1000 ]] && echo "ERROR: Idle time not reset: $NewIdle"
fi
# TODO: Check if screen is locked.
} # CheckToBlankOrLock
ResetIdleSeconds=0 # Total idle seconds we've invoked not host activity
SavedBlankSetting=false
SimulateUserActivity () {
# Is screen blanking and locking check turned off with parameters?
if [[ $fNoBlankLock == true ]] ; then
ScreenSaverCommand "SimulateUserActivity"
NewIdle="$(xprintidle)"
[[ $NewIdle -gt 1000 ]] && echo "ERROR: Idle time not reset: $NewIdle"
return 0
fi
if [[ $IdleSeconds -lt "$LowestSeconds" ]] ; then
# User activity on host is resetting idle time so no need for us to.
ResetIdleSeconds=0
else
# xidle seconds is greater than or equal to remote terminal activity
ResetIdleSeconds=$(( ResetIdleSeconds + IdleSeconds ))
CheckToBlankOrLock # Each time called also simulates user activity
fi
} # SimulateUserActivity
main () {
ParseParms "$@"
LowestSeconds=999999
if [[ $fDebug == true ]] ; then
LoopCnt=10 # Fake user inactivity for ten minutes
fi
while : ; do # Loop forever
Init # Support user changing idle time settings on server
GetWish # Get idle times using 'w -ish'
# Is a local user working at server or did we set last idle activity?
IdleSeconds=$(( $(xprintidle) / 1000 )) # returns milliseconds
[[ $fDebug == true ]] && echo IdleSeconds: $IdleSeconds \
LowestSeconds: $LowestSeconds
# If xprintidle time is less than SLEEP_SECS yes there is
# If xprintidle time is equal to last override time then no
# If Wish Seconds < SLEEP_SECS there was activity from remote user
if [[ $LowestSeconds -lt "$SLEEP_SECS" ]] ; then
SimulateUserActivity
IdleSeconds=0
else
IdleSeconds=$(( IdleSeconds + SLEEP_SECS ))
# Send wall message 10, 5, 3, 2 and 1 minutes before disconnect.
HostShutDownMessage
fi
sleep "$SLEEP_SECS"
done
} # main
main "$@"
ssh-activity -d使用时主机上的示例结果
========== ssh-activity.log Sun Jun 21 20:09:36 MDT 2020 ==========
IdleSeconds: 300 LowestSeconds: 338
rick pts/1 192.168.0.12 Wish Time: 1:42m Seconds: 6120
rick pts/19 192.168.0.12 Wish Time: 6:38 Seconds: 398
IdleSeconds: 360 LowestSeconds: 398
rick pts/1 192.168.0.12 Wish Time: 1:43m Seconds: 6180
rick pts/19 192.168.0.12 Wish Time: 7:38 Seconds: 458
IdleSeconds: 420 LowestSeconds: 458
rick pts/1 192.168.0.12 Wish Time: 1:44m Seconds: 6240
rick pts/19 192.168.0.12 Wish Time: 8:38 Seconds: 518
IdleSeconds: 480 LowestSeconds: 518
Broadcast message from rick@dell (somewhere) (Sun Jun 21 20:09:43 2020):
If no activity, shutdown in: 10 minute(s).
Broadcast message from rick@dell (somewhere) (Sun Jun 21 20:38:45 2020):
If no activity, shutdown in: 5 minute(s).
Broadcast message from rick@dell (somewhere) (Sun Jun 21 20:40:45 2020):
If no activity, shutdown in: 3 minute(s).
Broadcast message from rick@dell (somewhere) (Sun Jun 21 20:41:45 2020):
If no activity, shutdown in: 2 minute(s).
Broadcast message from rick@dell (somewhere) (Sun Jun 21 20:42:45 2020):
If no activity, shutdown in: 1 minute(s).
Broadcast message from rick@dell (somewhere) (Sun Jun 21 20:43:45 2020):
HOST SYSTEM SHUTDOWN at: Sun Jun 21 20:43:45 MDT 2020
我开发了一个脚本,叫做
ssh-activity
:w
每 60 秒监控一次命令的输出。dbus
呼叫模拟用户活动。wall
命令)警告关闭或挂起。ssh-activity
脚本ssh-activity -d
使用时主机上的示例结果关机时远程终端输出
如果远程用户未在终端中键入任何内容,则这些消息将在关机前 60、30、15、10、5、3、2、1 和 0 分钟出现(在我的情况下为暂停)。