Skip to content

Instantly share code, notes, and snippets.

@QNimbus
Created July 5, 2025 12:01
Show Gist options
  • Save QNimbus/55db05d8efbd95fc07dca55ca65b88b6 to your computer and use it in GitHub Desktop.
Save QNimbus/55db05d8efbd95fc07dca55ca65b88b6 to your computer and use it in GitHub Desktop.
Unifi troubleshooting script #unifi
#!/bin/bash
#
# unifi_log_collector.sh
# A script to collect logs and diagnostics from a UniFi device, with options for aggregation and tailing.
#
# Author: B. van Wetten
# Date: 2025-07-05
#
# Usage:
# 1. Make the script executable: chmod +x unifi_log_collector.sh
# 2. Set the password env var: export UNIFI_PASSWORD="your_device_password"
# 3. Run the script with the device's IP/hostname:
# ./unifi_log_collector.sh 192.168.1.1
#
# Optional Flags:
# --user <username> : The SSH username to use (defaults to 'root').
# --aggregate : Combine all logs into a single file.
# --tail <N> : Get only the last N lines from commands and log files.
#
# Examples:
# ./unifi_log_collector.sh --user ubnt 192.168.1.20
# ./unifi_log_collector.sh --tail 100 192.168.1.1
# ./unifi_log_collector.sh 192.168.1.1 --tail 100 --aggregate
#
# --- Configuration ---
SSH_USER="root" # Default SSH user
AGGREGATE_MODE=false
TAIL_LINES=""
# --- Pre-flight Checks ---
# 1. Parse command-line arguments
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--user)
if [ -z "$2" ]; then echo "Error: --user option requires a username." >&2; exit 1; fi
SSH_USER="$2"
shift; shift
;;
--aggregate)
AGGREGATE_MODE=true
shift
;;
--tail)
if [ -z "$2" ]; then echo "Error: --tail option requires a number." >&2; exit 1; fi
TAIL_LINES="$2"
shift; shift
;;
*)
TARGET_HOST="$1"
shift
;;
esac
done
# 2. Check for the target host argument
if [ -z "$TARGET_HOST" ]; then
echo "Usage: $0 [--user <user>] [--aggregate] [--tail N] <unifi_device_ip_or_hostname>"
exit 1
fi
# ... (rest of the pre-flight checks are the same)
if [ -n "$TAIL_LINES" ] && ! [[ "$TAIL_LINES" =~ ^[1-9][0-9]*$ ]]; then echo "Error: Value for --tail must be a positive integer." >&2; exit 1; fi
if [ -z "$UNIFI_PASSWORD" ]; then echo "Error: UNIFI_PASSWORD env var not set." >&2; exit 1; fi
if ! command -v sshpass &> /dev/null; then echo "Error: 'sshpass' is not installed." >&2; exit 1; fi
# --- Main Script Logic ---
echo "Starting log collection for device: ${TARGET_HOST}"; echo "Using SSH user: ${SSH_USER}"
if [ "$AGGREGATE_MODE" = true ]; then echo "Aggregate mode: ON"; fi
if [ -n "$TAIL_LINES" ]; then echo "Tailing mode: ON (last ${TAIL_LINES} lines)"; fi
TIMESTAMP=$(date +%Y%m%d-%H%M%S); OUTPUT_DIR="./unifi-logs_${TARGET_HOST}_${TIMESTAMP}"; mkdir -p "${OUTPUT_DIR}"
echo "Output will be saved in: ${OUTPUT_DIR}"
export SSHPASS="$UNIFI_PASSWORD"
SSH_CMD="sshpass -e ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${SSH_USER}@${TARGET_HOST}"
SCP_CMD="sshpass -e scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
# --- Phase 1: Run remote commands ---
echo; echo "=== Phase 1: Running remote commands ==="
# Updated command list with detailed network diagnostics
COMMANDS_TO_RUN=(
"info" # UniFi-specific device info
"uptime" # System uptime and load averages
"dmesg" # Kernel ring buffer messages
"ifconfig -a" # Classic network interface configuration
"ip addr show" # Modern network interface configuration
"ip route show" # Kernel IP routing table
"ip neigh show" # **ARP table (Neighbor cache)**
"brctl show" # **Spanning Tree bridge info**
"mca-dump" # **Detailed UniFi device state (includes STP)**
"swctrl port-stat all" # **Switch port hardware statistics**
"ps auxwww" # Process list, shows what's running
"free -m" # Memory usage
"df -h" # Filesystem disk space usage
)
for cmd in "${COMMANDS_TO_RUN[@]}"; do
filename=$(echo "${cmd}" | tr ' ' '_').log
echo "--> Grabbing output of '${cmd}'..."
remote_cmd_to_run="${cmd}"
if [ -n "$TAIL_LINES" ]; then remote_cmd_to_run="${cmd} | tail -n ${TAIL_LINES}"; fi
${SSH_CMD} "${remote_cmd_to_run}" > "${OUTPUT_DIR}/${filename}" 2>/dev/null || echo " [Warning] Failed to run '${cmd}'. It may not exist on this device model."
done
# --- Phase 2: Download log files ---
# (This section remains the same)
echo; echo "=== Phase 2: Downloading log files and directories ==="
FILES_TO_COPY=( "/var/log/messages" "/etc/persistent/cfg/config.properties" )
for file_path in "${FILES_TO_COPY[@]}"; do
echo "--> Downloading file: ${file_path}..."
local_filename=$(basename "${file_path}")
if [ -n "$TAIL_LINES" ]; then
${SSH_CMD} "tail -n ${TAIL_LINES} ${file_path}" > "${OUTPUT_DIR}/${local_filename}" 2>/dev/null || echo " [Info] Could not tail ${file_path}."
else
${SCP_CMD} "${SSH_USER}@${TARGET_HOST}:${file_path}" "${OUTPUT_DIR}/" >/dev/null 2>&1 || echo " [Info] Could not download ${file_path}."
fi
done
DIRS_TO_COPY=( "/var/log/unifi" "/var/log/suricata" )
for dir_path in "${DIRS_TO_COPY[@]}"; do
echo "--> Downloading directory: ${dir_path}..."
${SCP_CMD} -r "${SSH_USER}@${TARGET_HOST}:${dir_path}" "${OUTPUT_DIR}/" >/dev/null 2>&1 || echo " [Info] Could not download directory ${dir_path}."
done
# --- Phase 3 & Final Summary ---
# (These sections remain the same)
if [ "$AGGREGATE_MODE" = true ]; then
echo; echo "=== Phase 3: Aggregating all logs into a single file ==="
AGGREGATE_FILE="${OUTPUT_DIR}/_AGGREGATED_LOGS.log"; echo "--> Creating aggregate file: ${AGGREGATE_FILE}"
find "${OUTPUT_DIR}" -type f | sort | while read -r file; do
relative_path="${file#${OUTPUT_DIR}/}"; echo -e "======================================================================\n M M M START OF: ${relative_path}\n======================================================================\n" >> "${AGGREGATE_FILE}"
cat "${file}" >> "${AGGREGATE_FILE}"; echo -e "\n----------------------------------------------------------------------\n Z Z Z END OF: ${relative_path}\n----------------------------------------------------------------------\n\n\n" >> "${AGGREGATE_FILE}"
done; echo "--> Aggregation complete."
fi
echo; echo "=========================================="; echo "Log collection complete!"
echo "All files have been saved to: ${OUTPUT_DIR}"
if [ "$AGGREGATE_MODE" = true ]; then echo "A combined log file is available at: ${AGGREGATE_FILE}"; fi
echo; echo "To easily share these logs, you can create a compressed archive:"; echo "tar -czvf ${OUTPUT_DIR}.tar.gz ${OUTPUT_DIR}"; echo "=========================================="
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment