Files
Bjorn/bjorn_usb_gadget.sh
infinition aac77a3e76 Add Loki and Sentinel utility classes for web API endpoints
- Implemented LokiUtils class with GET and POST endpoints for managing scripts, jobs, and payloads.
- Added SentinelUtils class with GET and POST endpoints for managing events, rules, devices, and notifications.
- Both classes include error handling and JSON response formatting.
2026-03-14 22:33:10 +01:00

431 lines
15 KiB
Bash

#!/bin/bash
# bjorn_usb_gadget.sh
# Runtime manager for the BJORN USB composite gadget
# Usage:
# ./bjorn_usb_gadget.sh -u Bring the gadget up
# ./bjorn_usb_gadget.sh -d Bring the gadget down
# ./bjorn_usb_gadget.sh -r Reset the gadget (down + up)
# ./bjorn_usb_gadget.sh -l Show detailed status
# ./bjorn_usb_gadget.sh -h Show help
#
# Notes:
# This script no longer installs or removes the USB gadget stack.
# Installation is handled by the BJORN installer.
# This tool is for runtime diagnostics and recovery only.
set -u
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
SCRIPT_VERSION="2.0"
LOG_DIR="/var/log/bjorn_install"
LOG_FILE="$LOG_DIR/bjorn_usb_gadget_$(date +%Y%m%d_%H%M%S).log"
USB_GADGET_SERVICE="usb-gadget.service"
USB_GADGET_SCRIPT="/usr/local/bin/usb-gadget.sh"
DNSMASQ_SERVICE="dnsmasq.service"
DNSMASQ_CONFIG="/etc/dnsmasq.d/usb0"
MODULES_LOAD_FILE="/etc/modules-load.d/usb-gadget.conf"
MODULES_FILE="/etc/modules"
INTERFACES_FILE="/etc/network/interfaces"
mkdir -p "$LOG_DIR" 2>/dev/null || true
touch "$LOG_FILE" 2>/dev/null || true
log() {
local level="$1"
shift
local message="[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*"
local color="$NC"
case "$level" in
ERROR) color="$RED" ;;
SUCCESS) color="$GREEN" ;;
WARNING) color="$YELLOW" ;;
INFO) color="$BLUE" ;;
SECTION) color="$CYAN" ;;
esac
printf '%s\n' "$message" >> "$LOG_FILE" 2>/dev/null || true
printf '%b%s%b\n' "$color" "$message" "$NC"
}
show_recent_logs() {
if command -v journalctl >/dev/null 2>&1 && systemctl list-unit-files --type=service | grep -q "^${USB_GADGET_SERVICE}"; then
log "INFO" "Recent ${USB_GADGET_SERVICE} logs:"
journalctl -u "$USB_GADGET_SERVICE" -n 20 --no-pager 2>/dev/null || true
fi
}
ensure_root() {
if [ "$(id -u)" -ne 0 ]; then
log "ERROR" "This command must be run as root. Please use sudo."
exit 1
fi
}
service_exists() {
systemctl list-unit-files --type=service 2>/dev/null | grep -q "^$1"
}
service_active() {
systemctl is-active --quiet "$1"
}
service_enabled() {
systemctl is-enabled --quiet "$1"
}
usb0_exists() {
ip link show usb0 >/dev/null 2>&1
}
print_divider() {
printf '%b%s%b\n' "$CYAN" "============================================================" "$NC"
}
detect_boot_paths() {
local cmdline=""
local config=""
if [ -f /boot/firmware/cmdline.txt ]; then
cmdline="/boot/firmware/cmdline.txt"
elif [ -f /boot/cmdline.txt ]; then
cmdline="/boot/cmdline.txt"
fi
if [ -f /boot/firmware/config.txt ]; then
config="/boot/firmware/config.txt"
elif [ -f /boot/config.txt ]; then
config="/boot/config.txt"
fi
printf '%s|%s\n' "$cmdline" "$config"
}
wait_for_condition() {
local description="$1"
local attempts="$2"
shift 2
local i=1
while [ "$i" -le "$attempts" ]; do
if "$@"; then
log "SUCCESS" "$description"
return 0
fi
log "INFO" "Waiting for $description ($i/$attempts)..."
sleep 1
i=$((i + 1))
done
log "WARNING" "$description not reached after ${attempts}s"
return 1
}
show_usage() {
echo -e "${GREEN}Usage: $0 [OPTIONS]${NC}"
echo -e "Options:"
echo -e " ${BLUE}-u${NC} Bring USB Gadget up"
echo -e " ${BLUE}-d${NC} Bring USB Gadget down"
echo -e " ${BLUE}-r${NC} Reset USB Gadget (down + up)"
echo -e " ${BLUE}-l${NC} List detailed USB Gadget status"
echo -e " ${BLUE}-h${NC} Show this help message"
echo -e ""
echo -e "Examples:"
echo -e " $0 -u Start the BJORN composite gadget"
echo -e " $0 -d Stop the BJORN composite gadget cleanly"
echo -e " $0 -r Reinitialize the gadget if RNDIS/HID is stuck"
echo -e " $0 -l Show services, usb0, /dev/hidg*, and boot config"
echo -e ""
echo -e "${YELLOW}This script no longer installs or removes USB Gadget.${NC}"
echo -e "${YELLOW}That part is handled by the BJORN installer.${NC}"
if [ "${1:-exit}" = "return" ]; then
return 0
fi
exit 0
}
list_usb_gadget_info() {
local boot_pair
local cmdline_file
local config_file
boot_pair="$(detect_boot_paths)"
cmdline_file="${boot_pair%%|*}"
config_file="${boot_pair##*|}"
print_divider
log "SECTION" "BJORN USB Gadget Status"
print_divider
log "INFO" "Expected layout: RNDIS usb0 + HID keyboard /dev/hidg0 + HID mouse /dev/hidg1"
log "INFO" "Script version: ${SCRIPT_VERSION}"
log "INFO" "Log file: ${LOG_FILE}"
print_divider
log "SECTION" "Service Status"
if service_exists "$USB_GADGET_SERVICE"; then
service_active "$USB_GADGET_SERVICE" && log "SUCCESS" "${USB_GADGET_SERVICE} is active" || log "WARNING" "${USB_GADGET_SERVICE} is not active"
service_enabled "$USB_GADGET_SERVICE" && log "SUCCESS" "${USB_GADGET_SERVICE} is enabled at boot" || log "WARNING" "${USB_GADGET_SERVICE} is not enabled at boot"
else
log "ERROR" "${USB_GADGET_SERVICE} is not installed on this system"
fi
if service_exists "$DNSMASQ_SERVICE"; then
service_active "$DNSMASQ_SERVICE" && log "SUCCESS" "${DNSMASQ_SERVICE} is active" || log "WARNING" "${DNSMASQ_SERVICE} is not active"
else
log "WARNING" "${DNSMASQ_SERVICE} is not installed"
fi
print_divider
log "SECTION" "Runtime Files"
[ -x "$USB_GADGET_SCRIPT" ] && log "SUCCESS" "${USB_GADGET_SCRIPT} is present and executable" || log "ERROR" "${USB_GADGET_SCRIPT} is missing or not executable"
[ -c /dev/hidg0 ] && log "SUCCESS" "/dev/hidg0 (keyboard) is available" || log "WARNING" "/dev/hidg0 (keyboard) is not present"
[ -c /dev/hidg1 ] && log "SUCCESS" "/dev/hidg1 (mouse) is available" || log "WARNING" "/dev/hidg1 (mouse) is not present"
if ip link show usb0 >/dev/null 2>&1; then
log "SUCCESS" "usb0 network interface exists"
ip -brief addr show usb0 2>/dev/null || true
else
log "WARNING" "usb0 network interface is missing"
fi
if [ -d /sys/kernel/config/usb_gadget/g1 ]; then
log "SUCCESS" "Composite gadget directory exists: /sys/kernel/config/usb_gadget/g1"
find /sys/kernel/config/usb_gadget/g1/functions -maxdepth 1 -mindepth 1 -type d 2>/dev/null || true
else
log "WARNING" "No active gadget directory found under /sys/kernel/config/usb_gadget/g1"
fi
print_divider
log "SECTION" "Boot Configuration"
if [ -n "$cmdline_file" ] && [ -f "$cmdline_file" ]; then
grep -q "modules-load=dwc2" "$cmdline_file" && log "SUCCESS" "dwc2 boot module load is configured in ${cmdline_file}" || log "WARNING" "dwc2 boot module load not found in ${cmdline_file}"
else
log "WARNING" "cmdline.txt not found"
fi
if [ -n "$config_file" ] && [ -f "$config_file" ]; then
grep -q "^dtoverlay=dwc2" "$config_file" && log "SUCCESS" "dtoverlay=dwc2 is present in ${config_file}" || log "WARNING" "dtoverlay=dwc2 not found in ${config_file}"
else
log "WARNING" "config.txt not found"
fi
[ -f "$DNSMASQ_CONFIG" ] && log "SUCCESS" "${DNSMASQ_CONFIG} exists" || log "WARNING" "${DNSMASQ_CONFIG} is missing"
[ -f "$MODULES_LOAD_FILE" ] && log "INFO" "${MODULES_LOAD_FILE} exists (64-bit style module loading)"
[ -f "$MODULES_FILE" ] && grep -q "^libcomposite" "$MODULES_FILE" && log "INFO" "libcomposite is referenced in ${MODULES_FILE}"
[ -f "$INTERFACES_FILE" ] && grep -q "^allow-hotplug usb0" "$INTERFACES_FILE" && log "INFO" "usb0 legacy interface config detected in ${INTERFACES_FILE}"
print_divider
log "SECTION" "Quick Recovery Hints"
log "INFO" "If RNDIS or HID is stuck, run: sudo $0 -r"
log "INFO" "If startup still fails, inspect logs with: sudo journalctl -u ${USB_GADGET_SERVICE} -f"
log "INFO" "If HID nodes never appear after installer changes, a reboot may still be required"
}
bring_usb_gadget_down() {
ensure_root
print_divider
log "SECTION" "Bringing USB gadget down"
print_divider
if service_exists "$USB_GADGET_SERVICE"; then
if service_active "$USB_GADGET_SERVICE"; then
log "INFO" "Stopping ${USB_GADGET_SERVICE}..."
if systemctl stop "$USB_GADGET_SERVICE"; then
log "SUCCESS" "Stopped ${USB_GADGET_SERVICE}"
else
log "ERROR" "Failed to stop ${USB_GADGET_SERVICE}"
show_recent_logs
return 1
fi
else
log "INFO" "${USB_GADGET_SERVICE} is already stopped"
fi
else
log "WARNING" "${USB_GADGET_SERVICE} is not installed, trying direct runtime cleanup"
if [ -x "$USB_GADGET_SCRIPT" ]; then
"$USB_GADGET_SCRIPT" stop >> "$LOG_FILE" 2>&1 || true
fi
fi
if [ -x "$USB_GADGET_SCRIPT" ] && [ -d /sys/kernel/config/usb_gadget/g1 ]; then
log "INFO" "Running direct gadget cleanup via ${USB_GADGET_SCRIPT} stop"
"$USB_GADGET_SCRIPT" stop >> "$LOG_FILE" 2>&1 || log "WARNING" "Direct cleanup reported a non-fatal issue"
fi
if ip link show usb0 >/dev/null 2>&1; then
log "INFO" "Bringing usb0 interface down"
ip link set usb0 down >> "$LOG_FILE" 2>&1 || log "WARNING" "usb0 could not be forced down (often harmless)"
else
log "INFO" "usb0 is already absent"
fi
[ -c /dev/hidg0 ] && log "WARNING" "/dev/hidg0 still exists after stop (may clear on next start/reboot)" || log "SUCCESS" "/dev/hidg0 is no longer exposed"
[ -c /dev/hidg1 ] && log "WARNING" "/dev/hidg1 still exists after stop (may clear on next start/reboot)" || log "SUCCESS" "/dev/hidg1 is no longer exposed"
ip link show usb0 >/dev/null 2>&1 && log "WARNING" "usb0 still exists after stop" || log "SUCCESS" "usb0 is no longer present"
}
bring_usb_gadget_up() {
ensure_root
print_divider
log "SECTION" "Bringing USB gadget up"
print_divider
if [ ! -x "$USB_GADGET_SCRIPT" ]; then
log "ERROR" "${USB_GADGET_SCRIPT} is missing. The gadget runtime is not installed."
return 1
fi
if service_exists "$USB_GADGET_SERVICE"; then
log "INFO" "Reloading systemd daemon"
systemctl daemon-reload >> "$LOG_FILE" 2>&1 || log "WARNING" "systemd daemon-reload reported an issue"
log "INFO" "Starting ${USB_GADGET_SERVICE}..."
if systemctl start "$USB_GADGET_SERVICE"; then
log "SUCCESS" "Start command sent to ${USB_GADGET_SERVICE}"
else
log "ERROR" "Failed to start ${USB_GADGET_SERVICE}"
show_recent_logs
return 1
fi
else
log "WARNING" "${USB_GADGET_SERVICE} is not installed, running ${USB_GADGET_SCRIPT} directly"
if "$USB_GADGET_SCRIPT" >> "$LOG_FILE" 2>&1; then
log "SUCCESS" "Runtime script executed directly"
else
log "ERROR" "Runtime script failed"
return 1
fi
fi
wait_for_condition "${USB_GADGET_SERVICE} to become active" 10 service_active "$USB_GADGET_SERVICE" || true
wait_for_condition "usb0 to appear" 12 usb0_exists || true
if service_exists "$DNSMASQ_SERVICE"; then
log "INFO" "Restarting ${DNSMASQ_SERVICE} to refresh DHCP on usb0"
systemctl restart "$DNSMASQ_SERVICE" >> "$LOG_FILE" 2>&1 || log "WARNING" "Failed to restart ${DNSMASQ_SERVICE}"
fi
[ -c /dev/hidg0 ] && log "SUCCESS" "/dev/hidg0 (keyboard) is ready" || log "WARNING" "/dev/hidg0 not present yet"
[ -c /dev/hidg1 ] && log "SUCCESS" "/dev/hidg1 (mouse) is ready" || log "WARNING" "/dev/hidg1 not present yet"
if ip link show usb0 >/dev/null 2>&1; then
log "SUCCESS" "usb0 is present"
ip -brief addr show usb0 2>/dev/null || true
else
log "WARNING" "usb0 is still missing after startup"
fi
log "INFO" "If HID is still missing after a clean start, a reboot can still be required depending on the board/kernel state"
}
reset_usb_gadget() {
ensure_root
print_divider
log "SECTION" "Resetting USB gadget (down + up)"
print_divider
bring_usb_gadget_down || log "WARNING" "Down phase reported an issue, continuing with recovery"
log "INFO" "Waiting 2 seconds before bringing the gadget back up"
sleep 2
bring_usb_gadget_up
}
display_main_menu() {
while true; do
clear
print_divider
echo -e "${CYAN} BJORN USB Gadget Runtime Manager v${SCRIPT_VERSION}${NC}"
print_divider
echo -e "${BLUE} 1.${NC} Bring USB Gadget up"
echo -e "${BLUE} 2.${NC} Bring USB Gadget down"
echo -e "${BLUE} 3.${NC} Reset USB Gadget (down + up)"
echo -e "${BLUE} 4.${NC} List detailed USB Gadget status"
echo -e "${BLUE} 5.${NC} Show help"
echo -e "${BLUE} 6.${NC} Exit"
echo -e ""
echo -e "${YELLOW}Note:${NC} installation/removal is no longer handled here."
echo -n -e "${GREEN}Choose an option (1-6): ${NC}"
read -r choice
case "$choice" in
1)
bring_usb_gadget_up
echo ""
read -r -p "Press Enter to return to the menu..."
;;
2)
bring_usb_gadget_down
echo ""
read -r -p "Press Enter to return to the menu..."
;;
3)
reset_usb_gadget
echo ""
read -r -p "Press Enter to return to the menu..."
;;
4)
list_usb_gadget_info
echo ""
read -r -p "Press Enter to return to the menu..."
;;
5)
show_usage return
echo ""
read -r -p "Press Enter to return to the menu..."
;;
6)
log "INFO" "Exiting BJORN USB Gadget Runtime Manager"
exit 0
;;
*)
log "ERROR" "Invalid option. Please choose between 1 and 6."
sleep 2
;;
esac
done
}
while getopts ":udrlhf" opt; do
case "$opt" in
u)
bring_usb_gadget_up
exit $?
;;
d)
bring_usb_gadget_down
exit $?
;;
r)
reset_usb_gadget
exit $?
;;
l)
list_usb_gadget_info
exit 0
;;
h)
show_usage
;;
f)
log "ERROR" "Option -f (install) has been removed. Use -u to bring the gadget up or -r to reset it."
show_usage
;;
\?)
log "ERROR" "Invalid option: -$OPTARG"
show_usage
;;
esac
done
if [ $OPTIND -eq 1 ]; then
display_main_menu
fi