Created
July 5, 2025 12:01
-
-
Save QNimbus/55db05d8efbd95fc07dca55ca65b88b6 to your computer and use it in GitHub Desktop.
Unifi troubleshooting script #unifi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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