322 lines
13 KiB
Bash
Executable File
322 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
# Pi Multi-FX Pedal — System Minimizer
|
|
#
|
|
# Profiles current resource usage, then strips away everything not needed
|
|
# for real-time guitar DSP. Run AFTER dietpi-postinstall.sh (or on any
|
|
# fresh DietPi/RPi OS install).
|
|
#
|
|
# This script is idempotent — safe to run multiple times. The uninstall
|
|
# list is documented below; review before running in production.
|
|
#
|
|
# Usage:
|
|
# sudo ./minimize.sh [--dry-run] [--aggressive]
|
|
#
|
|
# --dry-run Only profile and list, don't remove anything
|
|
# --aggressive Also remove firmware, locales, docs, Python test packages
|
|
#
|
|
# Target: < 60 MB idle RAM, < 500 MB disk
|
|
# ──────────────────────────────────────────────────────────────────────────────
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
DRY_RUN=false
|
|
AGGRESSIVE=false
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--dry-run) DRY_RUN=true ;;
|
|
--aggressive) AGGRESSIVE=true ;;
|
|
esac
|
|
done
|
|
|
|
# ── Colour helpers ───────────────────────────────────────────────────────────
|
|
info() { printf "\e[34m[INFO] %s\e[0m\n" "$*"; }
|
|
ok() { printf "\e[32m[ OK ] %s\e[0m\n" "$*"; }
|
|
warn() { printf "\e[33m[WARN] %s\e[0m\n" "$*"; }
|
|
err() { printf "\e[31m[FAIL] %s\e[0m\n" "$*"; }
|
|
|
|
# ── Root check ───────────────────────────────────────────────────────────────
|
|
if [[ $EUID -ne 0 ]]; then
|
|
err "This script must be run as root (sudo ./minimize.sh)"
|
|
exit 1
|
|
fi
|
|
|
|
info "========== Pi Multi-FX Pedal — System Minimizer =========="
|
|
[[ "$DRY_RUN" == true ]] && warn "DRY RUN MODE — no changes will be made"
|
|
echo ""
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
# Step 0: Baseline — record current state
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
info "Step 0: Recording baseline..."
|
|
|
|
BASELINE_FILE="/tmp/pedal-minimize-baseline-$(date +%s).txt"
|
|
{
|
|
echo "=== Baseline: $(date -Iseconds) ==="
|
|
echo ""
|
|
echo "--- Memory ---"
|
|
free -m
|
|
echo ""
|
|
echo "--- Disk ---"
|
|
df -h /
|
|
echo ""
|
|
echo "--- Top 30 packages by installed size ---"
|
|
dpkg-query -Wf '${Installed-Size}\t${Package}\n' 2>/dev/null | sort -rn | head -30
|
|
echo ""
|
|
echo "--- Enabled systemd services ---"
|
|
systemctl list-units --type=service --state=running --no-legend 2>/dev/null | awk '{print $1}' | sort
|
|
echo ""
|
|
echo "--- Boot time (systemd-analyze) ---"
|
|
systemd-analyze 2>/dev/null || echo "not available"
|
|
echo ""
|
|
echo "--- Kernel modules loaded ---"
|
|
lsmod | sort
|
|
} > "$BASELINE_FILE"
|
|
|
|
ok "Baseline saved to $BASELINE_FILE"
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
# Step 1: Remove unnecessary packages
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
info "Step 1: Removing unnecessary packages..."
|
|
|
|
# ── Packages we KNOW we don't need on a headless audio pedal ──────────────
|
|
PACKAGES_TO_PURGE=(
|
|
# Printing (no USB printers on a pedal)
|
|
cups cups-bsd cups-client cups-common cups-core-drivers cups-daemon cups-filters cups-ppdc cups-server-common
|
|
# Avahi/mDNS (not needed for local audio)
|
|
avahi-daemon avahi-autoipd avahi-utils
|
|
# ModemManager
|
|
modemmanager
|
|
# Triggerhappy (input daemon — we handle GPIO ourselves)
|
|
triggerhappy
|
|
# X11 / display server (headless — OLED is SPI/I2C)
|
|
xserver-xorg xserver-xorg-core xserver-xorg-legacy x11-common
|
|
# Desktop environments (shouldn't be installed on minimal DietPi, but just in case)
|
|
raspberrypi-ui-mods raspberrypi-net-mods
|
|
lxde lxde-common lxmenu-data lxterminal
|
|
# Office/libreoffice
|
|
libreoffice* liblibreoffice*
|
|
# Web browsers
|
|
chromium-browser chromium-browser-l10n chromium-codecs-ffmpeg
|
|
firefox-esr
|
|
# Games
|
|
minecraft-pi python3-pgzero
|
|
# Sound servers that conflict with JACK
|
|
pulseaudio pulseaudio-utils pulseaudio-module-bluetooth
|
|
pipewire pipewire-audio pipewire-pulse
|
|
# WiFi firmware (if wired-only — we prompt below)
|
|
# Removed conditionally if --aggressive
|
|
)
|
|
|
|
# Actually remove them
|
|
for pkg in "${PACKAGES_TO_PURGE[@]}"; do
|
|
if dpkg -l "$pkg" &>/dev/null 2>&1; then
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
info "[DRY RUN] Would purge: $pkg"
|
|
else
|
|
apt-get purge -y -qq "$pkg" 2>/dev/null || true
|
|
ok "Purged: $pkg"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# ── Aggressive mode: remove even more ──────────────────────────────────────
|
|
if [[ "$AGGRESSIVE" == true ]]; then
|
|
info "Aggressive mode: removing additional packages..."
|
|
|
|
AGGRESSIVE_PACKAGES=(
|
|
# Firmware (keep only what's needed for boot + I2S)
|
|
firmware-brcm80211 firmware-atheros firmware-iwlwifi firmware-realtek
|
|
# Locales (keep only en_US.UTF-8)
|
|
locales-all language-pack-* language-pack-en-base
|
|
# Documentation
|
|
man-db manpages info
|
|
# Python test packages (not needed at runtime)
|
|
python3-pytest python3-pytest-asyncio python3-pytest-mock
|
|
# X11 fonts
|
|
fonts-* xfonts-*
|
|
# Unused sound modules for hardware we don't have
|
|
alsa-base # we use our own /etc/asound.conf
|
|
# Unused Python packages on DietPi
|
|
python3-pygame python3-tk
|
|
)
|
|
|
|
for pkg in "${AGGRESSIVE_PACKAGES[@]}"; do
|
|
if dpkg -l "$pkg" &>/dev/null 2>&1; then
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
info "[DRY RUN] Would purge: $pkg"
|
|
else
|
|
apt-get purge -y -qq "$pkg" 2>/dev/null || true
|
|
ok "Purged: $pkg"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Remove unused locale data
|
|
if [[ "$DRY_RUN" == false ]]; then
|
|
localedef --list-archive 2>/dev/null | grep -v en_US | while read -r locale; do
|
|
localedef --delete-from-archive "$locale" 2>/dev/null || true
|
|
done
|
|
ok "Stripped non-English locales"
|
|
fi
|
|
fi
|
|
|
|
# Clean up
|
|
if [[ "$DRY_RUN" == false ]]; then
|
|
apt-get autoremove --purge -y -qq 2>/dev/null || true
|
|
apt-get autoclean -y -qq 2>/dev/null || true
|
|
ok "apt autoremove/autoclean complete"
|
|
fi
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
# Step 2: Disable unnecessary systemd services
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
info "Step 2: Disabling unnecessary systemd services..."
|
|
|
|
SERVICES_TO_DISABLE=(
|
|
avahi-daemon
|
|
triggerhappy
|
|
ModemManager
|
|
systemd-resolved
|
|
apt-daily.timer
|
|
apt-daily-upgrade.timer
|
|
man-db.timer
|
|
e2scrub_all.timer
|
|
fstrim.timer
|
|
)
|
|
|
|
for svc in "${SERVICES_TO_DISABLE[@]}"; do
|
|
if systemctl is-enabled "$svc" &>/dev/null 2>&1; then
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
info "[DRY RUN] Would disable: $svc"
|
|
else
|
|
systemctl disable --now "$svc" 2>/dev/null || true
|
|
ok "Disabled: $svc"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Mask pulseaudio completely (not just disable)
|
|
for unit in pulseaudio.service pulseaudio.socket; do
|
|
if systemctl list-unit-files "$unit" &>/dev/null 2>&1; then
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
info "[DRY RUN] Would mask: $unit"
|
|
else
|
|
systemctl mask "$unit" 2>/dev/null || true
|
|
ok "Masked: $unit"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
# Step 3: Remove unused kernel modules
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
info "Step 3: Removing unnecessary kernel modules..."
|
|
|
|
BLACKLIST_FILE="/etc/modprobe.d/pedal-blacklist.conf"
|
|
if [[ "$DRY_RUN" == false ]]; then
|
|
cat > "$BLACKLIST_FILE" <<'BLACKLIST'
|
|
# Pi Multi-FX Pedal — blacklist unused kernel modules
|
|
# Audio subsystems we don't use
|
|
blacklist snd_bcm2835
|
|
blacklist snd_usb_audio
|
|
blacklist snd_usbmidi_lib
|
|
blacklist snd_rawmidi
|
|
blacklist snd_seq_device
|
|
blacklist snd_hwdep
|
|
blacklist snd_hda_intel
|
|
blacklist snd_hda_codec
|
|
blacklist snd_hda_core
|
|
|
|
# Bluetooth — KEPT (wireless MIDI controllers, tuner apps)
|
|
# Do NOT blacklist bluetooth modules
|
|
|
|
# WiFi — KEPT (web UI, SSH, preset downloads)
|
|
# Do NOT blacklist wifi modules
|
|
|
|
# Unused input devices
|
|
blacklist joydev
|
|
blacklist uvcvideo
|
|
blacklist videobuf2_v4l2
|
|
|
|
# Unused storage
|
|
blacklist r8152 # Realtek USB NIC
|
|
blacklist xhci_pci # USB 3.0 (not needed on headless pedal)
|
|
BLACKLIST
|
|
ok "Kernel module blacklist: $BLACKLIST_FILE"
|
|
else
|
|
info "[DRY RUN] Would write: $BLACKLIST_FILE"
|
|
fi
|
|
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
# Step 4: Profile and measure
|
|
# ══════════════════════════════════════════════════════════════════════════════
|
|
|
|
info "Step 4: Profiling post-minimization footprint..."
|
|
|
|
AFTER_FILE="/tmp/pedal-minimize-after-$(date +%s).txt"
|
|
{
|
|
echo "=== After Minimize: $(date -Iseconds) ==="
|
|
echo ""
|
|
echo "--- Memory ---"
|
|
free -m
|
|
echo ""
|
|
echo "--- Disk ---"
|
|
df -h /
|
|
echo ""
|
|
echo "--- Top 10 packages by installed size ---"
|
|
dpkg-query -Wf '${Installed-Size}\t${Package}\n' 2>/dev/null | sort -rn | head -10
|
|
echo ""
|
|
echo "--- Running services ---"
|
|
systemctl list-units --type=service --state=running --no-legend 2>/dev/null | awk '{print $1}' | sort
|
|
echo ""
|
|
echo "--- systemd-analyze blame ---"
|
|
systemd-analyze blame 2>/dev/null | head -20 || echo "not available"
|
|
} > "$AFTER_FILE"
|
|
|
|
ok "Post-minimize profile saved to $AFTER_FILE"
|
|
|
|
# ── Compute and display deltas ────────────────────────────────────────────────
|
|
echo ""
|
|
info "========== Before / After Summary =========="
|
|
|
|
# RAM
|
|
BEFORE_MEM=$(grep "^Mem:" "$BASELINE_FILE" 2>/dev/null | awk '{print $4}')
|
|
AFTER_MEM=$(grep "^Mem:" "$AFTER_FILE" 2>/dev/null | awk '{print $4}')
|
|
BEFORE_MEM_TOTAL=$(grep "^Mem:" "$BASELINE_FILE" 2>/dev/null | awk '{print $2}')
|
|
echo " RAM: ${BEFORE_MEM:-?} → ${AFTER_MEM:-?} MB free (of ${BEFORE_MEM_TOTAL:-?} MB total)"
|
|
|
|
# Disk
|
|
BEFORE_DISK=$(grep "^/dev/root" "$BASELINE_FILE" 2>/dev/null | awk '{print $4}')
|
|
AFTER_DISK=$(grep "^/dev/root" "$AFTER_FILE" 2>/dev/null | awk '{print $4}')
|
|
echo " Disk: ${BEFORE_DISK:-?} → ${AFTER_DISK:-?} available"
|
|
|
|
# Package count
|
|
BEFORE_PKGS=$(dpkg-query -f '.\n' -W 2>/dev/null | wc -l)
|
|
echo " Packages: $BEFORE_PKGS installed (after removal)"
|
|
|
|
echo ""
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — no changes were made"
|
|
info "Re-run without --dry-run to apply changes"
|
|
else
|
|
ok "Minimization complete!"
|
|
info "Baseline: $BASELINE_FILE"
|
|
info "After: $AFTER_FILE"
|
|
echo ""
|
|
info "A reboot is recommended to fully apply kernel module blacklist:"
|
|
echo " sudo reboot"
|
|
echo ""
|
|
info "After reboot, verify footprint:"
|
|
echo " free -m | grep Mem"
|
|
echo " df -h /"
|
|
echo " systemd-analyze"
|
|
fi |