#!/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)
