#!/usr/bin/env bash # scripts/install/server.sh - install and setup all server related apps and environments set -euo pipefail ORIG_USER=${SUDO_USER:-$(id -un)} REF=${LESAVKA_REF:-master} # fallback while [[ $# -gt 0 ]]; do case $1 in -r|--ref) REF="$2"; shift 2 ;; -h|--help) echo "Usage: $0 [--ref ]"; exit 0 ;; *) echo "Unknown option: $1"; exit 1 ;; esac done echo "==> Using git ref: $REF" echo "==> 1a. Base packages" sudo pacman -Syq --needed --noconfirm git \ rustup \ protobuf \ gcc \ alsa-utils \ pipewire \ pipewire-pulse \ tailscale \ base-devel \ v4l-utils \ gstreamer \ gst-plugins-base \ gst-plugins-base-libs \ gst-plugins-good \ gst-plugins-bad \ gst-plugins-bad-libs \ gst-plugins-ugly \ gst-libav \ tcpdump \ lsof if ! command -v yay >/dev/null 2>&1; then echo "==> 1b. installing yay from AUR ..." sudo -u "$ORIG_USER" bash -c ' cd /tmp && git clone --depth 1 https://aur.archlinux.org/yay.git && cd yay && makepkg -si --noconfirm' fi # yay -S --noconfirm grpcurl-bin echo "==> 1c. GPIO permissions for relay" echo 'z /dev/gpiochip* 0660 root gpio -' | sudo tee /etc/tmpfiles.d/gpiochip.conf >/dev/null sudo systemd-tmpfiles --create /etc/tmpfiles.d/gpiochip.conf || true echo "==> 2a. Kernel-driver tweaks" cat <<'EOF' | sudo tee /etc/modprobe.d/gc311-stream.conf >/dev/null options uvcvideo quirks=0x200 timeout=10000 EOF echo "==> 2b. Predictable /dev names for each capture card" # ensure relay (GPIO power) is on if present if systemctl list-unit-files | grep -q '^relay.service'; then sudo systemctl enable --now relay.service sleep 2 fi # probe v4l2 devices for GC311s (07ca:3311) mapfile -t GC_VIDEOS < <( sudo v4l2-ctl --list-devices 2>/dev/null | awk '/Live Gamer MINI/{getline; print $1}' ) # fallback via udev if v4l2-ctl output is empty/partial if [ "${#GC_VIDEOS[@]}" -ne 2 ]; then mapfile -t GC_VIDEOS < <( for dev in /dev/video*; do props=$(sudo udevadm info -q property -n "$dev" 2>/dev/null || true) if echo "$props" | grep -q 'ID_VENDOR_ID=07ca' && echo "$props" | grep -q 'ID_MODEL_ID=3311'; then echo "$dev" fi done | sort -u ) fi if [ "${#GC_VIDEOS[@]}" -ne 2 ]; then echo "❌ Exactly two GC311 capture cards (index0) must be attached!" >&2 printf ' Detected: %s\n' "${GC_VIDEOS[@]}" exit 1 fi mapfile -t TAGS < <( for v in "${GC_VIDEOS[@]}"; do sudo udevadm info -q property -n "$v" | awk -F= '/^ID_PATH_TAG=/{print $2}' done ) printf ' ↪ Left card: %s (%s)\n' "${GC_VIDEOS[0]}" "${TAGS[0]}" printf ' ↪ Right card: %s (%s)\n' "${GC_VIDEOS[1]}" "${TAGS[1]}" LEFT_TAG=${TAGS[0]} RIGHT_TAG=${TAGS[1]} sudo tee /etc/udev/rules.d/85-gc311.rules >/dev/null < 3. Rust toolchain" sudo rustup default stable sudo -u "$ORIG_USER" rustup default stable echo "==> 4a. Source checkout" SRC_DIR=/var/src/lesavka REPO_URL=ssh://git@scm.bstein.dev:2242/bstein/lesavka.git if [[ ! -d $SRC_DIR ]]; then sudo mkdir -p /var/src sudo chown "$ORIG_USER":"$ORIG_USER" /var/src fi if [[ -d $SRC_DIR/.git ]]; then sudo -u "$ORIG_USER" git -C "$SRC_DIR" fetch --all --tags --prune else sudo -u "$ORIG_USER" git clone "$REPO_URL" "$SRC_DIR" fi if sudo -u "$ORIG_USER" git -C "$SRC_DIR" rev-parse --verify --quiet "origin/$REF" >/dev/null; then sudo -u "$ORIG_USER" git -C "$SRC_DIR" checkout -B "$REF" "origin/$REF" else sudo -u "$ORIG_USER" git -C "$SRC_DIR" checkout --force "$REF" fi echo "==> 4b. Source build" sudo -u "$ORIG_USER" bash -c "cd '$SRC_DIR/server' && cargo clean && cargo build --release" echo "==> 5. Install binaries" sudo install -Dm755 "$SRC_DIR/server/target/release/lesavka-server" /usr/local/bin/lesavka-server sudo install -Dm755 "$SRC_DIR/scripts/daemon/lesavka-core.sh" /usr/local/bin/lesavka-core.sh echo "==> 6a. Systemd units - lesavka-core" cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-core.service >/dev/null [Unit] Description=lesavka USB gadget bring-up After=sys-kernel-config.mount Requires=sys-kernel-config.mount [Service] Type=oneshot ExecStart=/usr/local/bin/lesavka-core.sh RemainAfterExit=yes CapabilityBoundingSet=CAP_SYS_ADMIN CAP_SYS_MODULE AmbientCapabilities=CAP_SYS_MODULE MountFlags=slave [Install] WantedBy=multi-user.target UNIT echo "==> 6b. Systemd units - lesavka-server" cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-server.service >/dev/null [Unit] Description=lesavka gRPC relay After=network.target lesavka-core.service [Service] ExecStart=/usr/local/bin/lesavka-server Restart=always Environment=RUST_LOG=lesavka_server=info,lesavka_server::audio=info,lesavka_server::video=debug,lesavka_server::gadget=info Environment=RUST_BACKTRACE=1 Environment=GST_DEBUG="*:2,alsasink:6,alsasrc:6" Restart=always RestartSec=5 StandardError=append:/tmp/lesavka-server.stderr StartLimitIntervalSec=30 StartLimitBurst=10 User=root [Install] WantedBy=multi-user.target UNIT echo "==> 6c. Systemd units - initialization" sudo truncate -s 0 /tmp/lesavka-server.log sudo systemctl daemon-reload sudo systemctl enable --now lesavka-core sudo systemctl restart lesavka-core echo "✅ lesavka-core installed and restarted..." sudo systemctl enable --now lesavka-server sudo systemctl restart lesavka-server echo "✅ lesavka-server installed and restarted..."