mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 13:23:15 +02:00
fix: bluetooth optimze arch desktop phone foucs mode and no secres
This commit is contained in:
parent
bf4a8ce173
commit
253b327b72
@ -335,6 +335,17 @@ repos:
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
|
||||
# ===========================================================================
|
||||
# SECRET PATTERNS - Block commits containing sensitive data
|
||||
# ===========================================================================
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: check-no-secrets
|
||||
name: check for leaked secrets
|
||||
entry: scripts/check_no_secrets.sh
|
||||
language: script
|
||||
exclude: ^(\.secret-patterns|\.pre-commit-config\.yaml|.*\.geojson)$
|
||||
|
||||
# ===========================================================================
|
||||
# COMMITIZEN - Conventional commits (optional)
|
||||
# ===========================================================================
|
||||
|
||||
@ -8,11 +8,15 @@
|
||||
# 3. Stale pairing data causing connection hangs
|
||||
# 4. Missing Broadcom firmware (.hcd files)
|
||||
# 5. Stuck/unresponsive adapter requiring USB reset
|
||||
# 6. USB autosuspend causing audio dropouts
|
||||
# 7. Hung PipeWire/WirePlumber audio stack
|
||||
# 8. Auto scan/pair/trust/connect when MAC is provided
|
||||
# 9. SBC-XQ codec causing dropouts on older adapters
|
||||
#
|
||||
# Usage:
|
||||
# ./fix_bluetooth.sh # Diagnose and fix all issues
|
||||
# ./fix_bluetooth.sh --interactive # Prompt before each fix
|
||||
# ./fix_bluetooth.sh <MAC> # Target a specific device
|
||||
# ./fix_bluetooth.sh <MAC> # Fix + auto-connect to device
|
||||
# ./fix_bluetooth.sh --interactive <MAC> # Both
|
||||
#
|
||||
# Safe to re-run: all fixes are idempotent.
|
||||
@ -61,6 +65,14 @@ apply_fix() {
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helper: run a bluetoothctl command reliably via stdin pipe.
|
||||
# (bluetoothctl -- <cmd> returns empty when run non-interactively)
|
||||
# ---------------------------------------------------------------------------
|
||||
_btctl() {
|
||||
echo "$*" | bluetoothctl 2>/dev/null
|
||||
}
|
||||
|
||||
# ==========================================================================
|
||||
# 1. Check Bluetooth service status
|
||||
# ==========================================================================
|
||||
@ -181,7 +193,7 @@ check_adapter_stuck() {
|
||||
|
||||
# Test if bluetoothctl can see the adapter
|
||||
local adapter_list
|
||||
adapter_list=$(echo "list" | bluetoothctl 2>/dev/null | grep "^Controller" || true)
|
||||
adapter_list=$(_btctl list | grep "^Controller" || true)
|
||||
|
||||
if [[ -n $adapter_list ]]; then
|
||||
log_ok "Adapter is responsive: $adapter_list"
|
||||
@ -224,16 +236,19 @@ remove_stale_pairing() {
|
||||
echo ""
|
||||
log_info "Checking for stale pairing with $TARGET_MAC..."
|
||||
|
||||
if bluetoothctl info "$TARGET_MAC" 2>/dev/null | grep -q "Device $TARGET_MAC"; then
|
||||
local info
|
||||
info=$(_btctl info "$TARGET_MAC" || true)
|
||||
|
||||
if echo "$info" | grep -q "Device $TARGET_MAC"; then
|
||||
local paired
|
||||
paired=$(bluetoothctl info "$TARGET_MAC" 2>/dev/null | grep "Paired:" | awk '{print $2}')
|
||||
paired=$(echo "$info" | grep "Paired:" | awk '{print $2}')
|
||||
local connected
|
||||
connected=$(bluetoothctl info "$TARGET_MAC" 2>/dev/null | grep "Connected:" | awk '{print $2}')
|
||||
connected=$(echo "$info" | grep "Connected:" | awk '{print $2}')
|
||||
|
||||
if [[ $paired == "yes" && $connected == "no" ]]; then
|
||||
log_warn "Device is paired but NOT connected — may have stale pairing."
|
||||
apply_fix "Removing stale pairing for $TARGET_MAC" \
|
||||
bluetoothctl remove "$TARGET_MAC"
|
||||
_btctl remove "$TARGET_MAC"
|
||||
elif [[ $paired == "no" ]]; then
|
||||
log_info "Device is not currently paired."
|
||||
else
|
||||
@ -258,7 +273,244 @@ restart_bluetooth() {
|
||||
}
|
||||
|
||||
# ==========================================================================
|
||||
# 7. Show connection instructions
|
||||
# 7. Disable USB autosuspend for Bluetooth adapter
|
||||
# ==========================================================================
|
||||
fix_usb_autosuspend() {
|
||||
echo ""
|
||||
log_info "Checking USB autosuspend for Bluetooth adapter..."
|
||||
|
||||
local bt_usb
|
||||
bt_usb=$(lsusb 2>/dev/null | grep -i bluetooth | head -1 || true)
|
||||
if [[ -z $bt_usb ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local usb_id
|
||||
usb_id=$(echo "$bt_usb" | grep -oP 'ID \K[0-9a-f]{4}:[0-9a-f]{4}' || true)
|
||||
if [[ -z $usb_id ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local vendor="${usb_id%%:*}"
|
||||
local product="${usb_id##*:}"
|
||||
|
||||
# Find the sysfs device path
|
||||
local sysfs_path=""
|
||||
local dev_path
|
||||
for dev_path in /sys/bus/usb/devices/*/; do
|
||||
if [[ -f "${dev_path}idVendor" && -f "${dev_path}idProduct" ]]; then
|
||||
local v p
|
||||
v=$(cat "${dev_path}idVendor" 2>/dev/null || true)
|
||||
p=$(cat "${dev_path}idProduct" 2>/dev/null || true)
|
||||
if [[ $v == "$vendor" && $p == "$product" ]]; then
|
||||
sysfs_path="$dev_path"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z $sysfs_path ]]; then
|
||||
log_warn "Could not find sysfs path for BT adapter."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local power_control="${sysfs_path}power/control"
|
||||
if [[ -f $power_control ]]; then
|
||||
local current
|
||||
current=$(cat "$power_control" 2>/dev/null || true)
|
||||
if [[ $current != "on" ]]; then
|
||||
log_warn "USB autosuspend is enabled ($current) — can cause audio dropouts."
|
||||
apply_fix "Disabling USB autosuspend for BT adapter" \
|
||||
_disable_usb_autosuspend "$power_control" "$vendor" "$product"
|
||||
else
|
||||
log_ok "USB autosuspend already disabled."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
_disable_usb_autosuspend() {
|
||||
local power_control="$1"
|
||||
local vendor="$2"
|
||||
local product="$3"
|
||||
|
||||
# Immediate fix
|
||||
echo "on" > "$power_control"
|
||||
|
||||
# Persistent udev rule
|
||||
local rule_file="/etc/udev/rules.d/50-bluetooth-no-autosuspend.rules"
|
||||
local rule="ACTION==\"add\", SUBSYSTEM==\"usb\", ATTR{idVendor}==\"$vendor\", ATTR{idProduct}==\"$product\", ATTR{power/control}=\"on\""
|
||||
|
||||
if [[ ! -f $rule_file ]] || ! grep -qF "$vendor" "$rule_file" 2>/dev/null; then
|
||||
echo "$rule" > "$rule_file"
|
||||
udevadm control --reload-rules 2>/dev/null || true
|
||||
log_info "Created persistent udev rule: $rule_file"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==========================================================================
|
||||
# 8. Check PipeWire/WirePlumber health (hung audio stack)
|
||||
# ==========================================================================
|
||||
check_pipewire_health() {
|
||||
echo ""
|
||||
log_info "Checking PipeWire/WirePlumber health..."
|
||||
|
||||
if ! has_cmd wpctl; then
|
||||
log_info "wpctl not found — skipping PipeWire health check."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Test if PipeWire is responding within 3 seconds
|
||||
if timeout 3 wpctl status &>/dev/null; then
|
||||
log_ok "PipeWire is responsive."
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warn "PipeWire/WirePlumber appears hung (wpctl timed out)."
|
||||
apply_fix "Restarting PipeWire + WirePlumber audio stack" \
|
||||
_restart_pipewire_stack
|
||||
}
|
||||
|
||||
_restart_pipewire_stack() {
|
||||
# Restart as the calling user (these are user services)
|
||||
local target_user
|
||||
target_user="${SUDO_USER:-$USER}"
|
||||
local target_uid
|
||||
target_uid=$(id -u "$target_user")
|
||||
|
||||
sudo -u "$target_user" \
|
||||
XDG_RUNTIME_DIR="/run/user/$target_uid" \
|
||||
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$target_uid/bus" \
|
||||
systemctl --user restart pipewire pipewire-pulse wireplumber
|
||||
|
||||
sleep 3
|
||||
log_info "Waiting for audio stack to initialize..."
|
||||
}
|
||||
|
||||
# ==========================================================================
|
||||
# 9. Auto-connect target device
|
||||
# ==========================================================================
|
||||
connect_device() {
|
||||
if [[ -z $TARGET_MAC ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Attempting to connect to $TARGET_MAC..."
|
||||
|
||||
# Check if already connected
|
||||
local info
|
||||
info=$(_btctl info "$TARGET_MAC" || true)
|
||||
if echo "$info" | grep -q "Connected: yes"; then
|
||||
log_ok "Device $TARGET_MAC is already connected."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Power on adapter
|
||||
_btctl power on >/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# ---- Attempt 1: direct connect (existing pairing) ----
|
||||
if echo "$info" | grep -q "Paired: yes"; then
|
||||
log_info "Device is already paired. Trying direct connect..."
|
||||
{ echo "power on"; sleep 1; echo "connect $TARGET_MAC"; sleep 15; } \
|
||||
| bluetoothctl 2>/dev/null || true
|
||||
|
||||
info=$(_btctl info "$TARGET_MAC" || true)
|
||||
if echo "$info" | grep -q "Connected: yes"; then
|
||||
log_ok "Connected to $TARGET_MAC!"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Direct connect failed — remove stale pairing and try fresh
|
||||
log_warn "Direct connect failed. Removing stale pairing for fresh start."
|
||||
_btctl remove "$TARGET_MAC" >/dev/null || true
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# ---- Attempt 2: scan + pair from scratch ----
|
||||
log_info "Scanning for $TARGET_MAC (20 seconds)..."
|
||||
log_info "Make sure the device is in pairing mode."
|
||||
{ echo "power on"; sleep 1; echo "scan on"; sleep 20; echo "scan off"; sleep 2; } \
|
||||
| bluetoothctl 2>/dev/null || true
|
||||
|
||||
# Check if device was found
|
||||
local devices
|
||||
devices=$(_btctl devices || true)
|
||||
if ! echo "$devices" | grep -qi "$TARGET_MAC"; then
|
||||
log_error "Device $TARGET_MAC not found during scan."
|
||||
log_info "Put the device in pairing mode and re-run the script."
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_ok "Device found during scan."
|
||||
|
||||
# Pair
|
||||
log_info "Pairing..."
|
||||
{ echo "power on"; sleep 1; echo "pair $TARGET_MAC"; sleep 5; } \
|
||||
| bluetoothctl 2>/dev/null || true
|
||||
|
||||
# Trust (so it auto-reconnects in the future)
|
||||
log_info "Trusting..."
|
||||
{ echo "trust $TARGET_MAC"; sleep 2; } | bluetoothctl 2>/dev/null || true
|
||||
|
||||
# Connect
|
||||
log_info "Connecting..."
|
||||
{ echo "power on"; sleep 1; echo "connect $TARGET_MAC"; sleep 15; } \
|
||||
| bluetoothctl 2>/dev/null || true
|
||||
|
||||
# Verify connection
|
||||
sleep 2
|
||||
info=$(_btctl info "$TARGET_MAC" || true)
|
||||
if echo "$info" | grep -q "Connected: yes"; then
|
||||
log_ok "Successfully connected to $TARGET_MAC!"
|
||||
else
|
||||
log_error "Connection to $TARGET_MAC failed."
|
||||
log_info "Try putting the device in pairing mode and re-run."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ==========================================================================
|
||||
# 10. Set audio profile (avoid SBC-XQ dropouts on older adapters)
|
||||
# ==========================================================================
|
||||
set_audio_profile() {
|
||||
if [[ -z $TARGET_MAC ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! has_cmd pactl; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Checking audio profile..."
|
||||
|
||||
# Wait a moment for PipeWire to set up the audio card
|
||||
sleep 3
|
||||
|
||||
local card_name="bluez_card.${TARGET_MAC//:/_}"
|
||||
local card_info
|
||||
card_info=$(pactl list cards 2>/dev/null || true)
|
||||
|
||||
if ! echo "$card_info" | grep -q "$card_name"; then
|
||||
log_info "No PipeWire audio card found for device (may not be an audio device)."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local current_profile
|
||||
current_profile=$(echo "$card_info" | grep -A 50 "$card_name" | grep "Active Profile:" | head -1 | awk '{print $3}' || true)
|
||||
|
||||
if [[ $current_profile == *"sbc_xq"* ]]; then
|
||||
log_warn "SBC-XQ codec active — may cause audio dropouts on older adapters."
|
||||
apply_fix "Switching to standard SBC codec" \
|
||||
pactl set-card-profile "$card_name" a2dp-sink
|
||||
elif [[ -n $current_profile ]]; then
|
||||
log_ok "Audio profile: $current_profile"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==========================================================================
|
||||
# 11. Show connection instructions
|
||||
# ==========================================================================
|
||||
show_instructions() {
|
||||
echo ""
|
||||
@ -294,7 +546,7 @@ EOF
|
||||
}
|
||||
|
||||
# ==========================================================================
|
||||
# 8. Dump diagnostic info
|
||||
# 12. Dump diagnostic info
|
||||
# ==========================================================================
|
||||
dump_diagnostics() {
|
||||
echo ""
|
||||
@ -302,11 +554,11 @@ dump_diagnostics() {
|
||||
|
||||
echo ""
|
||||
echo "--- Bluetooth adapter ---"
|
||||
echo "show" | bluetoothctl 2>/dev/null | grep -v '\[bluetoothctl\]' | head -20 || true
|
||||
_btctl show | grep -v '\[bluetoothctl\]' | head -20 || true
|
||||
|
||||
echo ""
|
||||
echo "--- Known devices ---"
|
||||
echo "devices" | bluetoothctl 2>/dev/null | grep '^Device' || true
|
||||
_btctl devices | grep '^Device' || true
|
||||
|
||||
echo ""
|
||||
echo "--- Loaded Bluetooth kernel modules ---"
|
||||
@ -319,7 +571,7 @@ dump_diagnostics() {
|
||||
if [[ -n $TARGET_MAC ]]; then
|
||||
echo ""
|
||||
echo "--- Device info: $TARGET_MAC ---"
|
||||
bluetoothctl info "$TARGET_MAC" 2>/dev/null || echo "(device not known)"
|
||||
_btctl info "$TARGET_MAC" || echo "(device not known)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
@ -336,7 +588,8 @@ main() {
|
||||
check_packages
|
||||
check_firmware
|
||||
check_adapter_stuck
|
||||
remove_stale_pairing
|
||||
fix_usb_autosuspend
|
||||
check_pipewire_health
|
||||
restart_bluetooth
|
||||
|
||||
echo ""
|
||||
@ -344,7 +597,11 @@ main() {
|
||||
printf "Fixes applied: %d | Skipped: %d\n" "$FIXES_APPLIED" "$FIXES_SKIPPED"
|
||||
echo "==========================================="
|
||||
|
||||
show_instructions
|
||||
if ! connect_device; then
|
||||
show_instructions
|
||||
fi
|
||||
|
||||
set_audio_profile
|
||||
}
|
||||
|
||||
main
|
||||
|
||||
623
linux_configuration/scripts/fixes/optimize_arch_desktop.sh
Executable file
623
linux_configuration/scripts/fixes/optimize_arch_desktop.sh
Executable file
@ -0,0 +1,623 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Optimize Arch Linux desktop for maximum performance on high-end hardware
|
||||
#
|
||||
# Tuning areas:
|
||||
# 1. CPU scheduler — performance governor on all cores
|
||||
# 2. I/O scheduler — optimal scheduler per drive type (none for NVMe, mq-deadline for SATA SSD)
|
||||
# 3. Memory / swap — lower swappiness, tune dirty page writeback for responsiveness
|
||||
# 4. Kernel network — TCP BBR, fastopen, larger buffers
|
||||
# 5. Filesystem — fstrim timer, noatime advisory
|
||||
# 6. NVIDIA GPU — max performance level via persistence mode
|
||||
# 7. Kernel mitigations — option to disable CPU vulnerability mitigations for extra speed
|
||||
# 8. Boot speed — disable unnecessary wait-online services
|
||||
# 9. Journal housekeeping — cap at 300M
|
||||
# 10. Process scheduler — install ananicy-cpp for automatic nice/ionice/scheduling
|
||||
#
|
||||
# Usage:
|
||||
# ./optimize_arch_desktop.sh # Apply safe optimizations
|
||||
# ./optimize_arch_desktop.sh --dry-run # Show what would be done
|
||||
# ./optimize_arch_desktop.sh --interactive # Prompt before each tweak
|
||||
# ./optimize_arch_desktop.sh --aggressive # Include CPU mitigation disable (risk: security)
|
||||
# ./optimize_arch_desktop.sh -h # Show help
|
||||
#
|
||||
# All tweaks are idempotent and safe to re-run.
|
||||
# Some kernel parameter changes require a reboot to take full effect.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
# shellcheck source=../lib/common.sh
|
||||
source "$SCRIPT_DIR/../lib/common.sh"
|
||||
|
||||
parse_interactive_args "$@"
|
||||
shift "$COMMON_ARGS_SHIFT"
|
||||
|
||||
DRY_RUN=false
|
||||
AGGRESSIVE=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
;;
|
||||
--aggressive)
|
||||
AGGRESSIVE=true
|
||||
;;
|
||||
-h | --help)
|
||||
cat <<'EOF'
|
||||
optimize_arch_desktop.sh - Squeeze maximum performance from an Arch Linux desktop
|
||||
|
||||
Usage: optimize_arch_desktop.sh [OPTIONS]
|
||||
|
||||
Options:
|
||||
--dry-run Show what would be done without making changes
|
||||
--aggressive Also disable CPU vulnerability mitigations (trades security for speed)
|
||||
-i, --interactive Prompt before each optimization
|
||||
-h, --help Show this help message
|
||||
|
||||
Optimizations applied:
|
||||
1. Set CPU governor to performance on all cores
|
||||
2. Set optimal I/O scheduler per drive (none/mq-deadline)
|
||||
3. Tune vm.swappiness, dirty ratios, vfs_cache_pressure via sysctl
|
||||
4. Enable TCP BBR congestion control + fastopen + buffer tuning
|
||||
5. Enable fstrim.timer for SSD TRIM maintenance
|
||||
6. Set NVIDIA GPU to max performance level (persistence mode)
|
||||
7. [--aggressive] Disable CPU vulnerability mitigations
|
||||
8. Disable NetworkManager-wait-online.service for faster boot
|
||||
9. Vacuum & cap systemd journal at 300M
|
||||
10. Install/enable ananicy-cpp for automatic process prioritization
|
||||
|
||||
All optimizations are idempotent. Re-run safely at any time.
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_root "$@"
|
||||
|
||||
print_setup_header "Arch Linux Desktop Performance Optimizer"
|
||||
|
||||
TWEAKS_APPLIED=0
|
||||
TWEAKS_SKIPPED=0
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helper: apply or preview a tweak
|
||||
# ---------------------------------------------------------------------------
|
||||
apply_tweak() {
|
||||
local description="$1"
|
||||
shift
|
||||
|
||||
echo ""
|
||||
log_info "$description"
|
||||
|
||||
if [[ $DRY_RUN == "true" ]]; then
|
||||
echo " [dry-run] Would run: $*"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||
if ! ask_yes_no " Apply this optimization?"; then
|
||||
log_warn "Skipped."
|
||||
((TWEAKS_SKIPPED++)) || true
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if "$@"; then
|
||||
log_ok "Done."
|
||||
((TWEAKS_APPLIED++)) || true
|
||||
else
|
||||
log_error "Failed (non-fatal, continuing)."
|
||||
fi
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 1. CPU Governor → performance
|
||||
# ===================================================================
|
||||
tweak_cpu_governor() {
|
||||
local gov_files
|
||||
gov_files=$(find /sys/devices/system/cpu -maxdepth 3 -name scaling_governor 2>/dev/null || true)
|
||||
|
||||
if [[ -z $gov_files ]]; then
|
||||
log_warn "No CPU governor sysfs files found — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check current state
|
||||
local all_performance=true
|
||||
local f
|
||||
for f in $gov_files; do
|
||||
if [[ $(cat "$f") != "performance" ]]; then
|
||||
all_performance=false
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $all_performance == "true" ]]; then
|
||||
log_ok "All CPU cores already on 'performance' governor — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
for f in $gov_files; do
|
||||
echo "performance" >"$f"
|
||||
done
|
||||
|
||||
# Make it persistent via a sysctl-style drop-in using udev rule
|
||||
local udev_rule="/etc/udev/rules.d/60-cpu-governor-performance.rules"
|
||||
if [[ ! -f $udev_rule ]]; then
|
||||
cat >"$udev_rule" <<'UDEVEOF'
|
||||
# Set CPU governor to performance on all cores at boot
|
||||
SUBSYSTEM=="module", DEVPATH=="*/cpu/*", ATTR{scaling_governor}=="*", ATTR{scaling_governor}="performance"
|
||||
UDEVEOF
|
||||
fi
|
||||
|
||||
# Also install cpupower hook as a more reliable persistence method
|
||||
local cpupower_conf="/etc/default/cpupower"
|
||||
if has_cmd cpupower; then
|
||||
if [[ ! -f $cpupower_conf ]] || ! grep -q "^governor='performance'" "$cpupower_conf" 2>/dev/null; then
|
||||
mkdir -p "$(dirname "$cpupower_conf")"
|
||||
cat >"$cpupower_conf" <<'CPUEOF'
|
||||
# /etc/default/cpupower — managed by optimize_arch_desktop.sh
|
||||
governor='performance'
|
||||
CPUEOF
|
||||
systemctl enable cpupower.service 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 2. I/O Scheduler per drive type
|
||||
# ===================================================================
|
||||
tweak_io_scheduler() {
|
||||
local changed=false
|
||||
|
||||
local block_dev
|
||||
for block_dev in /sys/block/sd* /sys/block/nvme* /sys/block/vd*; do
|
||||
[[ -d $block_dev ]] || continue
|
||||
local sched_file="$block_dev/queue/scheduler"
|
||||
[[ -f $sched_file ]] || continue
|
||||
|
||||
local dev_name
|
||||
dev_name=$(basename "$block_dev")
|
||||
local rotational
|
||||
rotational=$(cat "$block_dev/queue/rotational" 2>/dev/null || echo 1)
|
||||
local current
|
||||
current=$(sed 's/.*\[\(.*\)\].*/\1/' "$sched_file" 2>/dev/null || true)
|
||||
|
||||
local target
|
||||
if [[ $dev_name == nvme* ]]; then
|
||||
target="none"
|
||||
elif [[ $rotational -eq 0 ]]; then
|
||||
target="mq-deadline"
|
||||
else
|
||||
target="bfq"
|
||||
fi
|
||||
|
||||
if [[ $current == "$target" ]]; then
|
||||
log_ok "$dev_name: already using '$target' scheduler."
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "$target" >"$sched_file" 2>/dev/null || true
|
||||
log_info "$dev_name: scheduler changed from '$current' to '$target'."
|
||||
changed=true
|
||||
done
|
||||
|
||||
# Persist via udev rule
|
||||
local udev_rule="/etc/udev/rules.d/60-io-scheduler.rules"
|
||||
if [[ ! -f $udev_rule ]]; then
|
||||
cat >"$udev_rule" <<'IOEOF'
|
||||
# NVMe: no scheduler (multi-queue hardware handles it)
|
||||
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
|
||||
# SATA SSD: mq-deadline (low latency)
|
||||
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
|
||||
# HDD: BFQ (fair bandwidth allocation)
|
||||
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
|
||||
IOEOF
|
||||
fi
|
||||
|
||||
if [[ $changed == "false" ]]; then
|
||||
log_ok "All I/O schedulers already optimal."
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 3. Memory & swap tuning via sysctl
|
||||
# ===================================================================
|
||||
tweak_vm_sysctl() {
|
||||
local dropin="/etc/sysctl.d/90-desktop-performance.conf"
|
||||
|
||||
# Desktop workloads: low swappiness, aggressive VFS caching, tuned dirty ratios
|
||||
local -A params=(
|
||||
["vm.swappiness"]="10"
|
||||
["vm.vfs_cache_pressure"]="50"
|
||||
["vm.dirty_ratio"]="15"
|
||||
["vm.dirty_background_ratio"]="5"
|
||||
["vm.dirty_writeback_centisecs"]="1500"
|
||||
["vm.page-cluster"]="0"
|
||||
)
|
||||
|
||||
local needs_update=false
|
||||
local key
|
||||
for key in "${!params[@]}"; do
|
||||
local current
|
||||
current=$(sysctl -n "$key" 2>/dev/null || true)
|
||||
if [[ $current != "${params[$key]}" ]]; then
|
||||
needs_update=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $needs_update == "false" && -f $dropin ]]; then
|
||||
log_ok "VM sysctl parameters already tuned — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
cat >"$dropin" <<'VMEOF'
|
||||
# Desktop performance tuning — managed by optimize_arch_desktop.sh
|
||||
#
|
||||
# vm.swappiness=10 — prefer keeping data in RAM over swapping
|
||||
# vm.vfs_cache_pressure=50 — favor keeping inode/dentry caches (speeds up file operations)
|
||||
# vm.dirty_ratio=15 — allow up to 15% RAM dirty before synchronous writeback
|
||||
# vm.dirty_background_ratio=5 — start async writeback at 5% dirty
|
||||
# vm.dirty_writeback_centisecs=1500 — flush dirty pages every 15s (less I/O churn)
|
||||
# vm.page-cluster=0 — read one page at a time from swap (reduces latency on SSD)
|
||||
vm.swappiness = 10
|
||||
vm.vfs_cache_pressure = 50
|
||||
vm.dirty_ratio = 15
|
||||
vm.dirty_background_ratio = 5
|
||||
vm.dirty_writeback_centisecs = 1500
|
||||
vm.page-cluster = 0
|
||||
VMEOF
|
||||
|
||||
sysctl --system >/dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 4. Network: TCP BBR + fastopen + buffer tuning
|
||||
# ===================================================================
|
||||
tweak_network_sysctl() {
|
||||
local dropin="/etc/sysctl.d/91-desktop-network.conf"
|
||||
|
||||
# Check if BBR module is available
|
||||
if ! modprobe tcp_bbr 2>/dev/null; then
|
||||
log_warn "tcp_bbr kernel module unavailable — skipping network tuning."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local -A params=(
|
||||
["net.core.default_qdisc"]="fq"
|
||||
["net.ipv4.tcp_congestion_control"]="bbr"
|
||||
["net.ipv4.tcp_fastopen"]="3"
|
||||
["net.core.rmem_max"]="16777216"
|
||||
["net.core.wmem_max"]="16777216"
|
||||
["net.ipv4.tcp_rmem"]="4096 1048576 16777216"
|
||||
["net.ipv4.tcp_wmem"]="4096 1048576 16777216"
|
||||
["net.ipv4.tcp_mtu_probing"]="1"
|
||||
)
|
||||
|
||||
local needs_update=false
|
||||
local key
|
||||
for key in "${!params[@]}"; do
|
||||
local current
|
||||
current=$(sysctl -n "$key" 2>/dev/null || true)
|
||||
# Normalize whitespace for comparison (kernel uses tabs)
|
||||
current=$(echo "$current" | xargs)
|
||||
local expected
|
||||
expected=$(echo "${params[$key]}" | xargs)
|
||||
if [[ $current != "$expected" ]]; then
|
||||
needs_update=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $needs_update == "false" && -f $dropin ]]; then
|
||||
log_ok "Network sysctl parameters already tuned — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
cat >"$dropin" <<'NETEOF'
|
||||
# Network performance tuning — managed by optimize_arch_desktop.sh
|
||||
#
|
||||
# BBR congestion control — better throughput and lower latency than cubic
|
||||
# TCP fastopen — saves one RTT on repeated connections (both client and server)
|
||||
# Larger buffers — helps on high-bandwidth or high-latency links
|
||||
net.core.default_qdisc = fq
|
||||
net.ipv4.tcp_congestion_control = bbr
|
||||
net.ipv4.tcp_fastopen = 3
|
||||
net.core.rmem_max = 16777216
|
||||
net.core.wmem_max = 16777216
|
||||
net.ipv4.tcp_rmem = 4096 1048576 16777216
|
||||
net.ipv4.tcp_wmem = 4096 1048576 16777216
|
||||
net.ipv4.tcp_mtu_probing = 1
|
||||
NETEOF
|
||||
|
||||
sysctl --system >/dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 5. fstrim timer
|
||||
# ===================================================================
|
||||
tweak_fstrim() {
|
||||
if systemctl is-enabled fstrim.timer >/dev/null 2>&1; then
|
||||
log_ok "fstrim.timer already enabled — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
systemctl enable --now fstrim.timer
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 6. NVIDIA GPU — max performance
|
||||
# ===================================================================
|
||||
tweak_nvidia_gpu() {
|
||||
if ! has_cmd nvidia-smi; then
|
||||
log_info "nvidia-smi not found — skipping GPU tuning."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Enable persistence mode (keeps driver loaded, faster app launches)
|
||||
local persist_status
|
||||
persist_status=$(nvidia-smi --query-gpu=persistence_mode --format=csv,noheader 2>/dev/null | head -n 1 | xargs || true)
|
||||
if [[ $persist_status != "Enabled" ]]; then
|
||||
nvidia-smi -pm 1 >/dev/null 2>&1 || true
|
||||
log_info "NVIDIA persistence mode enabled."
|
||||
else
|
||||
log_ok "NVIDIA persistence mode already enabled."
|
||||
fi
|
||||
|
||||
# Set power management to prefer maximum performance
|
||||
# PowerMizerMode: 1 = prefer max perf
|
||||
nvidia-smi -gps 0 >/dev/null 2>&1 || true
|
||||
|
||||
# Persist via systemd service
|
||||
local service_file="/etc/systemd/system/nvidia-performance.service"
|
||||
if [[ ! -f $service_file ]]; then
|
||||
cat >"$service_file" <<'NVSVC'
|
||||
[Unit]
|
||||
Description=Set NVIDIA GPU to max performance mode
|
||||
After=nvidia-persistenced.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/nvidia-smi -pm 1
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
NVSVC
|
||||
systemctl daemon-reload
|
||||
systemctl enable nvidia-performance.service 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Ensure nvidia-persistenced is enabled
|
||||
if has_cmd nvidia-persistenced; then
|
||||
systemctl enable nvidia-persistenced.service 2>/dev/null || true
|
||||
if ! systemctl is-active nvidia-persistenced.service >/dev/null 2>&1; then
|
||||
systemctl start nvidia-persistenced.service 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 7. [AGGRESSIVE] Disable CPU vulnerability mitigations
|
||||
# ===================================================================
|
||||
tweak_mitigations() {
|
||||
if [[ $AGGRESSIVE != "true" ]]; then
|
||||
log_info "Skipping CPU mitigation disable (use --aggressive to enable)."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Detect boot loader
|
||||
local boot_method=""
|
||||
if [[ -d /boot/loader/entries ]]; then
|
||||
boot_method="systemd-boot"
|
||||
elif [[ -f /etc/default/grub ]]; then
|
||||
boot_method="grub"
|
||||
else
|
||||
log_warn "Could not detect boot loader — skipping mitigation tweak."
|
||||
log_info "Manually add 'mitigations=off' to your kernel command line for extra speed."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $boot_method == "systemd-boot" ]]; then
|
||||
local entry
|
||||
entry=$(find /boot/loader/entries -name '*.conf' -print -quit 2>/dev/null || true)
|
||||
if [[ -n $entry ]]; then
|
||||
if grep -q 'mitigations=off' "$entry" 2>/dev/null; then
|
||||
log_ok "mitigations=off already set in systemd-boot — skipping."
|
||||
return 0
|
||||
fi
|
||||
# Append to the options line
|
||||
sed -i '/^options / s/$/ mitigations=off/' "$entry"
|
||||
log_warn "Added mitigations=off to $entry. REBOOT REQUIRED."
|
||||
log_warn "This trades security for ~5-15% performance. Only for isolated desktops."
|
||||
fi
|
||||
elif [[ $boot_method == "grub" ]]; then
|
||||
if grep -q 'mitigations=off' /etc/default/grub 2>/dev/null; then
|
||||
log_ok "mitigations=off already set in GRUB — skipping."
|
||||
return 0
|
||||
fi
|
||||
sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="\(.*\)"/GRUB_CMDLINE_LINUX_DEFAULT="\1 mitigations=off"/' /etc/default/grub
|
||||
grub-mkconfig -o /boot/grub/grub.cfg
|
||||
log_warn "Added mitigations=off to GRUB config. REBOOT REQUIRED."
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 8. Disable NetworkManager-wait-online
|
||||
# ===================================================================
|
||||
tweak_nm_wait_online() {
|
||||
if ! systemctl is-enabled NetworkManager-wait-online.service >/dev/null 2>&1; then
|
||||
log_ok "NetworkManager-wait-online already disabled — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
systemctl disable NetworkManager-wait-online.service
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 9. Journal vacuum + permanent cap
|
||||
# ===================================================================
|
||||
tweak_journal() {
|
||||
local usage_line
|
||||
usage_line=$(journalctl --disk-usage 2>/dev/null || true)
|
||||
|
||||
local needs_vacuum=false
|
||||
if [[ $usage_line =~ ([0-9]+\.?[0-9]*)\ G ]]; then
|
||||
needs_vacuum=true
|
||||
fi
|
||||
|
||||
if [[ $needs_vacuum == "true" ]]; then
|
||||
journalctl --vacuum-size=300M
|
||||
else
|
||||
log_ok "Journal already under 1GiB."
|
||||
fi
|
||||
|
||||
local dropin_dir="/etc/systemd/journald.conf.d"
|
||||
local dropin_file="$dropin_dir/size-limit.conf"
|
||||
|
||||
if [[ -f $dropin_file ]] && grep -q 'SystemMaxUse=300M' "$dropin_file"; then
|
||||
log_ok "Journal size cap already configured."
|
||||
else
|
||||
mkdir -p "$dropin_dir"
|
||||
cat >"$dropin_file" <<'JOURNALEOF'
|
||||
[Journal]
|
||||
SystemMaxUse=300M
|
||||
JOURNALEOF
|
||||
systemctl restart systemd-journald
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 10. ananicy-cpp — automatic process nice/ionice/scheduler tuning
|
||||
# ===================================================================
|
||||
tweak_ananicy() {
|
||||
# ananicy-cpp is the C++ rewrite, available in the AUR via ananicy-cpp
|
||||
if systemctl is-enabled ananicy-cpp.service >/dev/null 2>&1; then
|
||||
log_ok "ananicy-cpp is already enabled — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if pacman -Qi ananicy-cpp >/dev/null 2>&1; then
|
||||
systemctl enable --now ananicy-cpp.service
|
||||
log_info "Enabled ananicy-cpp.service."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check for the original ananicy
|
||||
if pacman -Qi ananicy >/dev/null 2>&1; then
|
||||
if ! systemctl is-enabled ananicy.service >/dev/null 2>&1; then
|
||||
systemctl enable --now ananicy.service
|
||||
log_info "Enabled ananicy.service."
|
||||
else
|
||||
log_ok "ananicy is already enabled."
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "ananicy-cpp is not installed."
|
||||
log_info "Install from AUR for automatic per-process priority tuning:"
|
||||
log_info " yay -S ananicy-cpp cachyos-ananicy-rules-git"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# Apply all tweaks
|
||||
# ===================================================================
|
||||
main() {
|
||||
apply_tweak \
|
||||
"Tweak 1/10: Set CPU governor to performance on all cores" \
|
||||
tweak_cpu_governor
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 2/10: Optimize I/O scheduler per drive type" \
|
||||
tweak_io_scheduler
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 3/10: Tune VM/memory sysctl for desktop responsiveness" \
|
||||
tweak_vm_sysctl
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 4/10: Enable TCP BBR + fastopen + larger buffers" \
|
||||
tweak_network_sysctl
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 5/10: Enable fstrim.timer for SSD TRIM maintenance" \
|
||||
tweak_fstrim
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 6/10: NVIDIA GPU persistence mode + max performance" \
|
||||
tweak_nvidia_gpu
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 7/10: CPU vulnerability mitigations (--aggressive only)" \
|
||||
tweak_mitigations
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 8/10: Disable NetworkManager-wait-online (faster boot)" \
|
||||
tweak_nm_wait_online
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 9/10: Vacuum & cap systemd journal at 300M" \
|
||||
tweak_journal
|
||||
|
||||
apply_tweak \
|
||||
"Tweak 10/10: Enable ananicy-cpp process prioritization" \
|
||||
tweak_ananicy
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Summary
|
||||
# ---------------------------------------------------------------
|
||||
echo ""
|
||||
echo "=============================="
|
||||
echo " Desktop Optimization Summary"
|
||||
echo "=============================="
|
||||
|
||||
if [[ $DRY_RUN == "true" ]]; then
|
||||
log_info "Dry-run mode — no changes were made."
|
||||
else
|
||||
log_ok "Optimizations applied: $TWEAKS_APPLIED"
|
||||
if [[ $TWEAKS_SKIPPED -gt 0 ]]; then
|
||||
log_warn "Optimizations skipped: $TWEAKS_SKIPPED"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Advisory: check for noatime
|
||||
local root_mount_opts
|
||||
root_mount_opts=$(findmnt -n -o OPTIONS / 2>/dev/null || true)
|
||||
if [[ -n $root_mount_opts ]] && ! echo "$root_mount_opts" | grep -q 'noatime'; then
|
||||
log_info "Tip: Your root filesystem does not use 'noatime'."
|
||||
log_info " Adding 'noatime' to /etc/fstab can reduce unnecessary disk writes."
|
||||
log_info " (Change 'relatime' or 'atime' to 'noatime' in /etc/fstab, then reboot)"
|
||||
fi
|
||||
|
||||
if [[ $AGGRESSIVE == "true" ]]; then
|
||||
log_warn "Aggressive mode was used — mitigations=off trades security for speed."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Reboot recommended for kernel parameter and boot loader changes to take effect."
|
||||
log_info "Verify after reboot with: diagnose_arch_performance.sh"
|
||||
}
|
||||
|
||||
main
|
||||
@ -18,12 +18,11 @@ When outside that radius: all apps work normally.
|
||||
|
||||
Open Google Maps, right-click your apartment → copy the coordinates shown.
|
||||
|
||||
### 2. Edit `config.sh`
|
||||
### 2. Edit `config_secrets.sh`
|
||||
|
||||
```sh
|
||||
HOME_LAT="52.123456" # your latitude
|
||||
HOME_LON="21.098765" # your longitude
|
||||
RADIUS=500 # meters
|
||||
HOME_LAT="-48.876667" # your latitude
|
||||
HOME_LON="-123.393333" # your longitude
|
||||
```
|
||||
|
||||
### 3. (Optional) Adjust the whitelist in `config.sh`
|
||||
|
||||
@ -60,9 +60,9 @@ check_coords() {
|
||||
echo "ERROR: You must set your home coordinates in config.sh before deploying!"
|
||||
echo ""
|
||||
echo " 1. Find your coords on Google Maps (right-click your apartment)"
|
||||
echo " 2. Edit phone_focus_mode/config.sh:"
|
||||
echo " HOME_LAT=\"52.123456\""
|
||||
echo " HOME_LON=\"21.098765\""
|
||||
echo " 2. Edit phone_focus_mode/config_secrets.sh:"
|
||||
echo " HOME_LAT=\"-48.876667\""
|
||||
echo " HOME_LON=\"-123.393333\""
|
||||
exit 1
|
||||
fi
|
||||
echo " Home location: $lat, $lon"
|
||||
|
||||
46
scripts/check_no_secrets.sh
Executable file
46
scripts/check_no_secrets.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# Pre-commit hook: check that no staged file contains a secret pattern.
|
||||
# Patterns are read from .secret-patterns (one regex per line, # = comment).
|
||||
set -euo pipefail
|
||||
|
||||
PATTERNS_FILE=".secret-patterns"
|
||||
|
||||
if [ ! -f "$PATTERNS_FILE" ]; then
|
||||
# Try finding it relative to the git root
|
||||
GIT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
if [ -n "$GIT_ROOT" ] && [ -f "$GIT_ROOT/$PATTERNS_FILE" ]; then
|
||||
PATTERNS_FILE="$GIT_ROOT/$PATTERNS_FILE"
|
||||
else
|
||||
echo "Warning: $PATTERNS_FILE not found, skipping secret check."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
found=0
|
||||
# Build a temp file with non-comment, non-empty patterns
|
||||
TMPPATTERNS="$(mktemp)"
|
||||
trap 'rm -f "$TMPPATTERNS"' EXIT
|
||||
grep -v '^\s*#' "$PATTERNS_FILE" | grep -v '^\s*$' > "$TMPPATTERNS"
|
||||
|
||||
if [ ! -s "$TMPPATTERNS" ]; then
|
||||
echo "No secret patterns defined in $PATTERNS_FILE, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for file in "$@"; do
|
||||
# Skip binary files
|
||||
if file --brief --mime-encoding "$file" 2>/dev/null | grep -q binary; then
|
||||
continue
|
||||
fi
|
||||
if grep -En -f "$TMPPATTERNS" "$file" 2>/dev/null; then
|
||||
echo "^^^ SECRET PATTERN found in: $file"
|
||||
found=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$found" -eq 1 ]; then
|
||||
echo ""
|
||||
echo "ERROR: Committed files contain secret patterns from $PATTERNS_FILE"
|
||||
echo "Either remove the sensitive data or update $PATTERNS_FILE if this is a false positive."
|
||||
exit 1
|
||||
fi
|
||||
88
scripts/makepkg-manual-dlagent.sh
Executable file
88
scripts/makepkg-manual-dlagent.sh
Executable file
@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
# makepkg-manual-dlagent.sh — A makepkg download agent for the manual:// protocol.
|
||||
#
|
||||
# makepkg calls this as: makepkg-manual-dlagent.sh <url> <output_file>
|
||||
#
|
||||
# For Unreal Engine URLs, it opens the Epic download page in your browser,
|
||||
# watches ~/Downloads via inotifywait, and copies the finished file to the
|
||||
# expected output path — making `yay -Sua` seamless.
|
||||
#
|
||||
# For any other manual:// URL, it prints the original "please download manually"
|
||||
# message and watches ~/Downloads for a matching filename.
|
||||
#
|
||||
# Install:
|
||||
# 1. Place this script somewhere on your PATH or a known location.
|
||||
# 2. Add to ~/.makepkg.conf:
|
||||
# DLAGENTS+=('manual::/path/to/makepkg-manual-dlagent.sh %u %o')
|
||||
#
|
||||
# Dependencies: inotify-tools (inotifywait), xdg-open
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
URL="$1" # e.g. manual://Linux_Unreal_Engine_5.7.2.zip
|
||||
OUTPUT="$2" # e.g. /home/user/.cache/yay/unreal-engine-bin/Linux_Unreal_Engine_5.7.2.zip
|
||||
|
||||
DOWNLOAD_DIR="${XDG_DOWNLOAD_DIR:-$HOME/Downloads}"
|
||||
|
||||
# Strip the manual:// prefix to get the bare filename
|
||||
FILENAME="${URL#manual://}"
|
||||
|
||||
# If the output file already exists, nothing to do
|
||||
if [[ -f "$OUTPUT" ]]; then
|
||||
echo " -> File already exists: $OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If already sitting in ~/Downloads, just copy it
|
||||
if [[ -f "$DOWNLOAD_DIR/$FILENAME" ]]; then
|
||||
echo " -> Found $FILENAME in $DOWNLOAD_DIR, copying..."
|
||||
cp -- "$DOWNLOAD_DIR/$FILENAME" "$OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Determine which browser page to open based on the filename
|
||||
OPEN_URL=""
|
||||
case "$FILENAME" in
|
||||
Linux_Unreal_Engine_*.zip)
|
||||
OPEN_URL="https://www.unrealengine.com/linux"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo " ┌─────────────────────────────────────────────────────────────┐"
|
||||
echo " │ manual:// download agent │"
|
||||
echo " │ File needed: $FILENAME"
|
||||
echo " │ Destination: $OUTPUT"
|
||||
if [[ -n "$OPEN_URL" ]]; then
|
||||
echo " │ Download from: $OPEN_URL"
|
||||
fi
|
||||
echo " │ │"
|
||||
echo " │ Download the file in your browser. │"
|
||||
echo " │ It will be detected automatically from ~/Downloads. │"
|
||||
echo " │ Press Ctrl+C to abort. │"
|
||||
echo " └─────────────────────────────────────────────────────────────┘"
|
||||
echo ""
|
||||
|
||||
# Open browser if we have a URL
|
||||
if [[ -n "$OPEN_URL" ]]; then
|
||||
xdg-open "$OPEN_URL" 2>/dev/null &
|
||||
fi
|
||||
|
||||
# Escape dots in filename for inotifywait regex
|
||||
FILENAME_ESCAPED="${FILENAME//./\\.}"
|
||||
|
||||
# Watch ~/Downloads for the file to appear
|
||||
while true; do
|
||||
inotifywait -q -q -e close_write,moved_to \
|
||||
--include "${FILENAME_ESCAPED}$" \
|
||||
"$DOWNLOAD_DIR" 2>/dev/null || true
|
||||
|
||||
if [[ -f "$DOWNLOAD_DIR/$FILENAME" ]]; then
|
||||
# Brief pause to ensure the file is fully flushed
|
||||
sleep 2
|
||||
echo " -> Download complete: $DOWNLOAD_DIR/$FILENAME"
|
||||
cp -- "$DOWNLOAD_DIR/$FILENAME" "$OUTPUT"
|
||||
echo " -> Copied to $OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
Loading…
Reference in New Issue
Block a user