#!/bin/sh

po_domain="alterator-multiseat"
alterator_api_version=1

. alterator-sh-functions
. autologin-sh-functions
. shell-quote

# CACHE

CACHE="/var/cache/alterator/multiseat"
SEAT="/etc/alterator/multiseat/seats"
SAVE="/etc/alterator/multiseat/save"
PULSE="/etc/alterator/multiseat/pulse"
PULSE_SCRIPT="/etc/X11/xinit.d/90-multiseat-pulse.sh"

UID_MIN=$(grep '^UID_MIN' /etc/login.defs 2>/dev/null|sed -r 's,UID_MIN[[:space:]]+,,')
[ -z "$UID_MIN" ] && UID_MIN=500

config_save() {
    case "$in_pulse" in
        "#t"|"on") [ -f $PULSE ] || touch $PULSE ;;
        "#f") [ ! -f $PULSE ] || rm -f $PULSE ;;
    esac
    [ -d "$SAVE" ] || mkdir -p "$SAVE"
    rm -f $SAVE/*
    cp -r $SEAT/* $SAVE/
}

config_reset() {
    [ -d "$SAVE" ] || return
    rm -rf $SEAT/*
    cp -f $SAVE/* $SEAT/
}

cache_init() {
    [ -d "$CACHE" ] || mkdir -p "$CACHE"
    [ -d "$SEAT" ] || mkdir -p "$SEAT"
    [ -d "$SAVE" ] || mkdir -p "$SAVE"
}

cache_seat_add() {
    [ -n "$1" ] || return
    [ "$1" != "seat0" ] || return
    [ -f "$SEAT/$1" ] || echo "$1" | grep -e "^seat[a-zA-Z0-9_-]\+$" && touch "$SEAT/$1"
}

cache_seat_remove() {
    [ -n "$1" ] || return
    rm -rf "$SEAT/$1"
}

cache_seat_list() {
    for i in $(ls "$SEAT/"); do
        sa="-"
        if $(grep -q $i /etc/udev/rules.d/*); then
            sa="+"
        fi
        dc="$(wc -l $SEAT/$i | cut -d' ' -f1)"
        echo "$i" "[$sa] $i ($dc)"
    done
}

cache_seat_dev_add() {
    # $1 - seatname, $2 - devices
    [ -n "$1" -a -n "$2" ] || return
    local IFS=";"
    for k in $2; do
        echo "$k" >> "$SEAT/$1"
    done
}

cache_seat_dev_remove() {
    [ -n "$1" -a -n "$2" ] || return
    local devname=
    local IFS=";"
    for k in $2; do
        quote_sed_regexp_variable devname "$k"
        sed -i -e "/$devname/d" "$SEAT/$1"
    done
}

cache_seat_dev_list() {
    [ -n "$1" ] || return
    for i in $(cat "$SEAT/$1"); do
        echo "$i" "$(get_device_name $i)"
    done
}

cache_seat_rm_all() {
    rm -rf $SEAT/*
}

check_prefix() {
    local i
    for i in $(cat $2); do
        [ $1 == $i ] && continue
        if [[ $1 == "$i"* ]]; then
            return 1
        fi
    done
    return 0
}

dev_filter() {
    for i in $(cat $1); do
        check_prefix $i $1
        [ "$?" = 1 ] && echo "$i"
    done
}

dev_clean() {
    for i in $(cat $1); do
        for j in $(cat $2); do
            if [[ "$i" == "$j"* ]]; then
                echo $i
            fi
        done
    done
}

init_device_list() {
    >$CACHE/devices
    for s in $(loginctl list-seats | grep "^seat[a-zA-Z0-9_-]\+"); do
        loginctl seat-status "$s" | grep -o "/sys/devices/.*/drm/card[0-9]\+$" >>$CACHE/devices
        loginctl seat-status "$s" | grep -o "/sys/devices/.*/sound/card[0-9]\+$" >>$CACHE/devices
        loginctl seat-status "$s" | grep -o "/sys/devices/.*/usb[0-9]\+/[0-9-]\+[/0-9.-]*$" >>$CACHE/devices
    done
}

get_device_list() {
    init_device_list
    local f1=$(mktemp)
    local f2=$(mktemp)
    sort $CACHE/devices > $f1
    sort $SEAT/* > $f2
    local i devname
    for i in $(dev_filter $f2); do
        quote_sed_regexp_variable devname "$i"
        sed -i -e "/$devname/d" $SEAT/*
    done
    for i in $(dev_clean $f1 $f2); do
        quote_sed_regexp_variable devname "$i"
        sed -i -e "/$devname/d" $f1
    done
    for i in $(comm -23 $f1 $f2); do
        echo "$i" "$(get_device_name $i)"
    done
    rm -f $f1
    rm -f $f2
}

get_usb_name() {
    [ -n "$1" ] || return
    p="$1"
    local usb=$(echo $p | grep -o "usb[0-9]\+")
    local res="/ $(cat $p/product)"
    local devpath="$(cat $p/devpath)"
    while true; do
        p="$(dirname $p)"
        if $(echo "$p" | grep -q "/usb[0-9]\+$"); then
            break
        fi
        res="/ $(cat "$p/product") id: $(cat $p/devpath) $res "
    done
    echo "$usb $res id: $devpath"
}

get_device_name() {
    [ -n "$1" ] || return
    if [ ! -d "$1" ]; then 
        echo "`_ "[device not found]"`"
        return
    fi
    local code
    # get name from lspci
    if $(echo "$1" | grep -q "sound\|drm/card"); then
        code=$(echo $1 | awk -F/ '{print $(NF-2)}' | cut -d: -f2-)
        lspci | grep "^$code" | cut -d: -f3
    else
        # for USB devices read name from file
        get_usb_name "$1"
    fi
}

get_new_seat_name() {
    q="$(ls $SEAT | wc -l)"
    [ $q = 0 ] && q=1
    while true; do
        if [ -f "$SEAT/seat$q" ]; then
            ((q++))
        else
            echo "seat$q"
            break
        fi
    done
}

device_flush() {
    loginctl flush-devices
}

attach_fb() {
    [ -n "$1" ] || return
    local seat="$1"
    [ -n "$2" ] || return
    local device="$2"
    [ -z ${device##*/drm/card*} ] || return
    local fb=${device%%drm/card*}graphics/fb*/
    local f
    for f in $fb; do
        loginctl attach "$seat" "$f"
        sleep 1
    done
}

multiseat_activate() {
    local name=
    for s in $(ls $SEAT/*); do
        name="$(basename $s)"
        for d in $(cat "$s"); do
            loginctl attach "$name" "$d"
            attach_fb "$name" "$d"
            sleep 1
        done
    done
    udevadm trigger
    sync
    reboot
}

autologin_disable() {
    cat /etc/passwd |
    while IFS=':' read name password uid gid gecos home shell; do
        [ "$uid" -ge "$UID_MIN" ] || continue
        [ "$shell" == "/sbin/nologin" ] || grep -qs "^$shell$" /etc/shells || continue
        [ -x "$shell" ] || continue
        al_check "$name" && al_disable "$name"
    done 2>/dev/null
}

cache_init

on_message() {
  case "$in_action" in
    read)
        write_string_param seat_new_name "$(get_new_seat_name)"
        write_bool_param pulse "$(([ -f $PULSE ] && echo on) || echo off)"
        write_bool_param pulse_visible "$(([ -f $PULSE_SCRIPT ] && echo on) || echo off)"
        ;;
    write)
        case "$in__objects" in
            seat_add)
                cache_seat_add "$in_seatname"
            ;;
            seat_remove)
                cache_seat_remove "$in_seatname"
            ;;
            seat_dev_add)
                cache_seat_dev_add "$in_seatname" "$in_seatdev"
            ;;
            seat_dev_remove)
                cache_seat_dev_remove "$in_seatname" "$in_seatdev"
            ;;
            device_flush)
                device_flush
            ;;
            seat_rm_all)
                cache_seat_rm_all
            ;;
            activate)
                config_save
                autologin_disable
                multiseat_activate
            ;;
            reset)
                config_reset
            ;;
        esac
        ;;
    list)
        case "$in__objects" in
            seat_list)
                cache_seat_list | write_enum
            ;;
            device_list)
                get_device_list | write_enum
            ;;
            seat_dev_list)
                cache_seat_dev_list "$in_seatname" | write_enum
            ;;
        esac
        ;;
  esac
}

message_loop
