Initial commit: wer - replacement for w/who on Debian 13
Lists logged-in sessions (local and ssh), one entry per terminal, including multiplexed ssh connections (ControlMaster). Needed because Debian 13 no longer populates utmp, so w/who show nothing. - Sessions and origin via systemd-logind (loginctl) - Terminals detected via session leaders and cgroup mapping - sudo shells (own pty) shown indented below their terminal - BEFEHL column shows the current foreground process - Sessions without a terminal (scp, sftp, ssh -N) are listed too
This commit is contained in:
@@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# wer - ein Eintrag pro Terminal (lokal, ssh, auch bei ControlMaster/Multiplexing)
|
||||||
|
# Ersatz für 'w'/'who' unter Debian 13
|
||||||
|
# - sudo-Shells (eigenes Pseudo-Terminal) erscheinen eingerückt unter ihrem Terminal
|
||||||
|
# - Spalte BEFEHL: aktuell im Vordergrund laufender Befehl (leer = Shell wartet am Prompt)
|
||||||
|
|
||||||
|
declare -A from seen elev
|
||||||
|
rows=()
|
||||||
|
|
||||||
|
get_from() { # Herkunft einer logind-Sitzung (mit Cache)
|
||||||
|
local sid=$1 k v remote=no host=
|
||||||
|
[[ -n ${from[$sid]} ]] && return
|
||||||
|
while IFS== read -r k v; do
|
||||||
|
case $k in Remote) remote=$v ;; RemoteHost) host=$v ;; esac
|
||||||
|
done < <(loginctl show-session "$sid" -p Remote -p RemoteHost 2>/dev/null </dev/null)
|
||||||
|
if [[ $remote == yes ]]; then from[$sid]=${host:-remote}; else from[$sid]=lokal; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
session_of_pid() {
|
||||||
|
sed -n 's#.*/session-\([A-Za-z0-9]\+\)\.scope.*#\1#p' "/proc/$1/cgroup" 2>/dev/null | head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# alle Prozesse mit Terminal: pid ppid sid tty etimes pgid tpgid comm
|
||||||
|
procs=$(ps -eo pid=,ppid=,sid=,tty=,etimes=,pgid=,tpgid=,comm= -ww </dev/null | awk '$4!="?"')
|
||||||
|
now=$(date +%s)
|
||||||
|
mypg=$(ps -o pgid= -p $$ 2>/dev/null </dev/null | tr -d ' ') # eigene Gruppe (wer selbst) ausblenden
|
||||||
|
|
||||||
|
# Vordergrundbefehl eines Terminals: $1=tty $2=PID der Shell (wird übersprungen)
|
||||||
|
fgcmd() {
|
||||||
|
local pid ppid sess t et pg tpg comm a res=
|
||||||
|
while read -r pid ppid sess t et pg tpg comm; do
|
||||||
|
[[ $t == "$1" && $pg == "$tpg" && $pg != "$mypg" && $pid != "$2" ]] || continue
|
||||||
|
a=$(ps -o args= -p "$pid" -ww 2>/dev/null </dev/null)
|
||||||
|
[[ -n $a ]] && res+="${res:+ | }$a"
|
||||||
|
done <<<"$procs"
|
||||||
|
printf '%s' "${res:0:70}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- Durchgang 1: Terminals einsammeln, sudo-Terminals dem äußeren zuordnen ----
|
||||||
|
for tty in $(awk '{print $4}' <<<"$procs" | sort -uV); do
|
||||||
|
lsid= leader= lcomm= lppid= first= oldest=0
|
||||||
|
|
||||||
|
while read -r pid ppid sess t et pg tpg comm; do
|
||||||
|
[[ -z $first ]] && first=$comm
|
||||||
|
[[ -z $lsid ]] && lsid=$(session_of_pid "$pid")
|
||||||
|
(( et > oldest )) && oldest=$et
|
||||||
|
if [[ $pid == "$sess" ]]; then leader=$pid; lcomm=$comm; lppid=$ppid; fi
|
||||||
|
done < <(awk -v t="$tty" '$4==t' <<<"$procs")
|
||||||
|
|
||||||
|
[[ -n $lsid ]] || continue # kein Benutzer-Login
|
||||||
|
seit=$(date -d "@$(( now - oldest ))" '+%F %H:%M')
|
||||||
|
|
||||||
|
if [[ -n $lppid ]]; then
|
||||||
|
pcomm=$(ps -o comm= -p "$lppid" 2>/dev/null </dev/null)
|
||||||
|
if [[ $pcomm == sudo ]]; then
|
||||||
|
# Pseudo-Terminal von sudo -> unter dem äußeren Terminal anzeigen
|
||||||
|
outer=$(ps -o tty= -p "$lppid" 2>/dev/null </dev/null | tr -d ' ')
|
||||||
|
suser=$(ps -o user= -p "$leader" 2>/dev/null </dev/null | tr -d ' ')
|
||||||
|
read -r cpid child < <(ps -o pid=,comm= --ppid "$leader" 2>/dev/null </dev/null \
|
||||||
|
| grep -v ' sudo$' | head -1)
|
||||||
|
[[ -n $child ]] || { child=$lcomm; cpid=$leader; }
|
||||||
|
cmd=$(fgcmd "$tty" "$cpid")
|
||||||
|
[[ -n $outer ]] && elev[$outer]+="$suser|$tty|$seit|$child|$cmd"$'\n'
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
[[ $pcomm =~ ^(tmux|screen|SCREEN) ]] && continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
user=$(stat -c %U "/dev/$tty" 2>/dev/null)
|
||||||
|
get_from "$lsid"
|
||||||
|
seen[$lsid]=1
|
||||||
|
cmd=$(fgcmd "$tty" "$leader")
|
||||||
|
rows+=("$lsid|${user:-?}|$tty|${from[$lsid]}|$seit|${lcomm:-$first}|$cmd")
|
||||||
|
done
|
||||||
|
|
||||||
|
# Spaltenbreite für VON (IPv6-Adressen sind lang)
|
||||||
|
vw=6
|
||||||
|
for r in "${rows[@]}"; do
|
||||||
|
IFS='|' read -r _ _ _ v _ <<<"$r"
|
||||||
|
(( ${#v} > vw )) && vw=${#v}
|
||||||
|
done
|
||||||
|
fmt="%-8s %-12s %-9s %-${vw}s %-16s %-9s %s\n"
|
||||||
|
|
||||||
|
# ---- Durchgang 2: Ausgabe ----
|
||||||
|
printf "$fmt" SESSION BENUTZER TTY VON SEIT PROZESS BEFEHL
|
||||||
|
|
||||||
|
for r in "${rows[@]}"; do
|
||||||
|
IFS='|' read -r lsid user tty von seit proc cmd <<<"$r"
|
||||||
|
printf "$fmt" "$lsid" "$user" "$tty" "$von" "$seit" "$proc" "$cmd"
|
||||||
|
while IFS='|' read -r su st ss sc sb; do
|
||||||
|
[[ -n $su ]] && printf "$fmt" "" '\_ '"$su" "$st" "sudo" "$ss" "$sc" "$sb"
|
||||||
|
done <<<"${elev[$tty]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Sitzungen ohne Terminal (scp, sftp, ssh -N ...) ebenfalls anzeigen
|
||||||
|
while read -r id _ user _; do
|
||||||
|
[[ -n ${seen[$id]} ]] && continue
|
||||||
|
[[ $(loginctl show-session "$id" -p Class --value 2>/dev/null </dev/null) == user ]] || continue
|
||||||
|
get_from "$id"
|
||||||
|
since=$(loginctl show-session "$id" -p Timestamp --value </dev/null)
|
||||||
|
printf "$fmt" "$id" "$user" "-" "${from[$id]}" \
|
||||||
|
"$(date -d "$since" '+%F %H:%M' 2>/dev/null)" "-" "(kein Terminal)"
|
||||||
|
done < <(loginctl list-sessions --no-legend --no-pager </dev/null)
|
||||||
Reference in New Issue
Block a user