#!/bin/bash
#
# ssh-agent-session - manage the per-user shared OpenSSH agent.
#

set -u

SERVICE="ssh-agent-session.service"
SOCKET_NAME="${SSH_AGENT_SESSION_SOCKET:-ssh-agent.sock}"
RUNTIME_DIR="${XDG_RUNTIME_DIR:-}"

die()
{
    echo "ssh-agent-session: $*" >&2
    exit 1
}

require_runtime_dir()
{
    [ -n "$RUNTIME_DIR" ] || die "XDG_RUNTIME_DIR is not set"
}

socket_path()
{
    require_runtime_dir
    printf '%s/%s\n' "$RUNTIME_DIR" "$SOCKET_NAME"
}

service_start()
{
    require_runtime_dir
    systemctl --user start "$SERVICE"
}

service_stop()
{
    systemctl --user stop "$SERVICE"
}

service_restart()
{
    systemctl --user restart "$SERVICE"
}

service_status()
{
    require_runtime_dir

    local socket
    socket="$(socket_path)"

    echo "Socket: $socket"

    if systemctl --user is-active --quiet "$SERVICE"; then
        echo "Agent:  running"
        systemctl --user show "$SERVICE" \
            --property=MainPID \
            --property=ActiveEnterTimestamp

        if SSH_AUTH_SOCK="$socket" ssh-add -l >/dev/null 2>&1; then
            echo "Socket: accessible"
            local keys
            keys="$(SSH_AUTH_SOCK="$socket" ssh-add -l 2>/dev/null | wc -l)"
            echo "Keys:    $keys"
        else
            echo "Socket: not accessible"
            return 3
        fi
    else
        echo "Agent:  stopped"
        return 3
    fi
}

print_env()
{
    printf 'export SSH_AUTH_SOCK=%q\n' "$(socket_path)"
}

ensure_agent()
{
    require_runtime_dir

    local socket
    socket="$(socket_path)"

    # If our socket already works, don't restart anything.
    if SSH_AUTH_SOCK="$socket" ssh-add -l >/dev/null 2>&1; then
        return 0
    fi

    systemctl --user start "$SERVICE"

    # Give ssh-agent a moment to create its socket.
    for _ in 1 2 3 4 5; do
        if SSH_AUTH_SOCK="$socket" ssh-add -l >/dev/null 2>&1; then
            return 0
        fi
        sleep 0.1
    done

    die "agent did not become available at $socket"
}

add_keys()
{
    ensure_agent
    local socket
    socket="$(socket_path)"
    SSH_AUTH_SOCK="$socket" ssh-add "$@"
}

list_keys()
{
    ensure_agent
    local socket
    socket="$(socket_path)"
    SSH_AUTH_SOCK="$socket" ssh-add -l
}

case "${1:-}" in
    start)
        service_start
        ;;
    stop)
        service_stop
        ;;
    restart)
        service_restart
        ;;
    status)
        service_status
        ;;
    socket)
        socket_path
        ;;
    env)
        print_env
        ;;
    add)
        shift
        [ "$#" -gt 0 ] || die "add requires at least one key"
        add_keys "$@"
        ;;
    list)
        list_keys
        ;;
    *)
        cat >&2 <<'EOF'
Usage:
  ssh-agent-session start
  ssh-agent-session stop
  ssh-agent-session restart
  ssh-agent-session status
  ssh-agent-session socket
  ssh-agent-session env
  ssh-agent-session add KEY...
  ssh-agent-session list
EOF
        exit 2
        ;;
esac
