mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 13:23:15 +02:00
pre commit fixes
This commit is contained in:
parent
aa8501cc49
commit
9e4fee54d7
@ -821,7 +821,7 @@ auto_remove_virtualbox_vms() {
|
||||
echo -e "${RED} Failed to remove: ${vm_name}${NC}" >&2
|
||||
((++failed))
|
||||
fi
|
||||
done <<< "$vm_list"
|
||||
done <<<"$vm_list"
|
||||
|
||||
echo -e "${CYAN}VM removal complete: ${success} removed, ${failed} failed.${NC}" >&2
|
||||
}
|
||||
|
||||
@ -15,29 +15,29 @@ NC='\033[0m' # No Color
|
||||
|
||||
# Auto-sudo functionality with confirmation
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "${YELLOW}This script requires root privileges to configure VirtualBox VMs.${NC}"
|
||||
echo -e "${CYAN}Executing with sudo...${NC}"
|
||||
exec sudo bash "$0" "$@"
|
||||
echo -e "${YELLOW}This script requires root privileges to configure VirtualBox VMs.${NC}"
|
||||
echo -e "${CYAN}Executing with sudo...${NC}"
|
||||
exec sudo bash "$0" "$@"
|
||||
fi
|
||||
|
||||
# Determine the real (non-root) user who invoked this script.
|
||||
# VBoxManage must run as this user because VMs are registered per-user.
|
||||
REAL_USER="${SUDO_USER:-$USER}"
|
||||
if [[ $REAL_USER == "root" ]]; then
|
||||
echo -e "${RED}Cannot determine the real user. Do not run this script as root directly.${NC}"
|
||||
echo -e "${RED}Run it as a normal user (it will auto-sudo as needed).${NC}"
|
||||
exit 1
|
||||
echo -e "${RED}Cannot determine the real user. Do not run this script as root directly.${NC}"
|
||||
echo -e "${RED}Run it as a normal user (it will auto-sudo as needed).${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if VBoxManage is available
|
||||
if ! command -v VBoxManage > /dev/null 2>&1; then
|
||||
echo -e "${RED}VBoxManage not found. VirtualBox may not be installed.${NC}"
|
||||
exit 1
|
||||
if ! command -v VBoxManage >/dev/null 2>&1; then
|
||||
echo -e "${RED}VBoxManage not found. VirtualBox may not be installed.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run VBoxManage as the real user so it sees their registered VMs
|
||||
vboxmanage_as_user() {
|
||||
sudo -u "$REAL_USER" VBoxManage "$@"
|
||||
sudo -u "$REAL_USER" VBoxManage "$@"
|
||||
}
|
||||
|
||||
# Configuration
|
||||
@ -46,54 +46,54 @@ HOSTS_ENFORCEMENT_MARKER="/var/lib/vbox-hosts-enforced"
|
||||
|
||||
# Get list of all VMs
|
||||
get_all_vms() {
|
||||
vboxmanage_as_user list vms | awk -F'"' '{print $2}'
|
||||
vboxmanage_as_user list vms | awk -F'"' '{print $2}'
|
||||
}
|
||||
|
||||
# Get list of running VMs
|
||||
get_running_vms() {
|
||||
vboxmanage_as_user list runningvms | awk -F'"' '{print $2}'
|
||||
vboxmanage_as_user list runningvms | awk -F'"' '{print $2}'
|
||||
}
|
||||
|
||||
# Configure a VM to use host DNS (NAT network)
|
||||
configure_vm_dns() {
|
||||
local vm_name="$1"
|
||||
echo -e "${BLUE}Configuring DNS for VM: ${vm_name}${NC}"
|
||||
local vm_name="$1"
|
||||
echo -e "${BLUE}Configuring DNS for VM: ${vm_name}${NC}"
|
||||
|
||||
# Enable DNS proxy for NAT adapter (adapter 1 by default)
|
||||
# This makes the VM use the host's DNS resolution
|
||||
vboxmanage_as_user modifyvm "$vm_name" --natdnshostresolver1 on 2>/dev/null || true
|
||||
vboxmanage_as_user modifyvm "$vm_name" --natdnsproxy1 on 2>/dev/null || true
|
||||
# Enable DNS proxy for NAT adapter (adapter 1 by default)
|
||||
# This makes the VM use the host's DNS resolution
|
||||
vboxmanage_as_user modifyvm "$vm_name" --natdnshostresolver1 on 2>/dev/null || true
|
||||
vboxmanage_as_user modifyvm "$vm_name" --natdnsproxy1 on 2>/dev/null || true
|
||||
|
||||
echo -e "${GREEN}DNS configuration applied to ${vm_name}${NC}"
|
||||
echo -e "${GREEN}DNS configuration applied to ${vm_name}${NC}"
|
||||
}
|
||||
|
||||
# Add shared folder for /etc directory (read-only)
|
||||
configure_hosts_shared_folder() {
|
||||
local vm_name="$1"
|
||||
echo -e "${BLUE}Setting up /etc/hosts sharing for VM: ${vm_name}${NC}"
|
||||
local vm_name="$1"
|
||||
echo -e "${BLUE}Setting up /etc/hosts sharing for VM: ${vm_name}${NC}"
|
||||
|
||||
# Remove existing shared folder if present
|
||||
vboxmanage_as_user sharedfolder remove "$vm_name" --name "$VBOX_SHARED_FOLDER_NAME" 2>/dev/null || true
|
||||
# Remove existing shared folder if present
|
||||
vboxmanage_as_user sharedfolder remove "$vm_name" --name "$VBOX_SHARED_FOLDER_NAME" 2>/dev/null || true
|
||||
|
||||
# Add /etc as a shared folder (read-only)
|
||||
vboxmanage_as_user sharedfolder add "$vm_name" \
|
||||
--name "$VBOX_SHARED_FOLDER_NAME" \
|
||||
--hostpath "/etc" \
|
||||
--readonly \
|
||||
--automount 2>/dev/null || {
|
||||
echo -e "${YELLOW}Could not add shared folder to ${vm_name} (VM may be running)${NC}"
|
||||
return 1
|
||||
}
|
||||
# Add /etc as a shared folder (read-only)
|
||||
vboxmanage_as_user sharedfolder add "$vm_name" \
|
||||
--name "$VBOX_SHARED_FOLDER_NAME" \
|
||||
--hostpath "/etc" \
|
||||
--readonly \
|
||||
--automount 2>/dev/null || {
|
||||
echo -e "${YELLOW}Could not add shared folder to ${vm_name} (VM may be running)${NC}"
|
||||
return 1
|
||||
}
|
||||
|
||||
echo -e "${GREEN}Shared folder configured for ${vm_name}${NC}"
|
||||
return 0
|
||||
echo -e "${GREEN}Shared folder configured for ${vm_name}${NC}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Create a startup script that can be placed in VMs
|
||||
generate_vm_startup_script() {
|
||||
local output_file="${1:-/tmp/vbox_hosts_sync.sh}"
|
||||
local output_file="${1:-/tmp/vbox_hosts_sync.sh}"
|
||||
|
||||
cat > "$output_file" << 'EOF'
|
||||
cat >"$output_file" <<'EOF'
|
||||
#!/bin/bash
|
||||
# VirtualBox VM startup script to sync /etc/hosts from host machine
|
||||
# This should be placed in the VM and run at startup
|
||||
@ -161,283 +161,283 @@ if [ -f "$HOST_HOSTS_FILE" ]; then
|
||||
fi
|
||||
EOF
|
||||
|
||||
chmod +x "$output_file"
|
||||
echo -e "${GREEN}Generated VM startup script at ${output_file}${NC}"
|
||||
echo -e "${CYAN}Copy this script to your VMs and add it to their startup (e.g., /etc/rc.local or systemd)${NC}"
|
||||
chmod +x "$output_file"
|
||||
echo -e "${GREEN}Generated VM startup script at ${output_file}${NC}"
|
||||
echo -e "${CYAN}Copy this script to your VMs and add it to their startup (e.g., /etc/rc.local or systemd)${NC}"
|
||||
}
|
||||
|
||||
# Get the disk image path for a VM (first SATA/IDE .vdi/.vmdk/.vhd)
|
||||
get_vm_disk_path() {
|
||||
local vm_name="$1"
|
||||
vboxmanage_as_user showvminfo "$vm_name" --machinereadable 2>/dev/null \
|
||||
| grep -E '^"(SATA|IDE|SCSI|NVMe)-[0-9]+-[0-9]+"=' \
|
||||
| grep -vE '="none"$' \
|
||||
| grep -vE '\.iso"$' \
|
||||
| head -1 \
|
||||
| sed 's/^[^=]*="//; s/"$//'
|
||||
local vm_name="$1"
|
||||
vboxmanage_as_user showvminfo "$vm_name" --machinereadable 2>/dev/null |
|
||||
grep -E '^"(SATA|IDE|SCSI|NVMe)-[0-9]+-[0-9]+"=' |
|
||||
grep -vE '="none"$' |
|
||||
grep -vE '\.iso"$' |
|
||||
head -1 |
|
||||
sed 's/^[^=]*="//; s/"$//'
|
||||
}
|
||||
|
||||
# Inject host's /etc/hosts directly into a VM disk image using qemu-nbd.
|
||||
# This is the only reliable way to enforce blocking, because NAT DNS proxy
|
||||
# alone does not work when the guest browser uses DNS-over-HTTPS (DoH).
|
||||
inject_hosts_into_vm_disk() {
|
||||
local vm_name="$1"
|
||||
local disk_path
|
||||
disk_path="$(get_vm_disk_path "$vm_name")"
|
||||
local vm_name="$1"
|
||||
local disk_path
|
||||
disk_path="$(get_vm_disk_path "$vm_name")"
|
||||
|
||||
if [[ -z $disk_path || ! -f $disk_path ]]; then
|
||||
echo -e "${YELLOW}Could not find disk image for VM '${vm_name}', skipping hosts injection${NC}"
|
||||
return 1
|
||||
fi
|
||||
if [[ -z $disk_path || ! -f $disk_path ]]; then
|
||||
echo -e "${YELLOW}Could not find disk image for VM '${vm_name}', skipping hosts injection${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Ensure VM is not running
|
||||
if vboxmanage_as_user list runningvms 2>/dev/null | grep -q "\"${vm_name}\""; then
|
||||
echo -e "${YELLOW}VM '${vm_name}' is running, cannot inject hosts file. Stop it first.${NC}"
|
||||
return 1
|
||||
fi
|
||||
# Ensure VM is not running
|
||||
if vboxmanage_as_user list runningvms 2>/dev/null | grep -q "\"${vm_name}\""; then
|
||||
echo -e "${YELLOW}VM '${vm_name}' is running, cannot inject hosts file. Stop it first.${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check for qemu-nbd
|
||||
if ! command -v qemu-nbd > /dev/null 2>&1; then
|
||||
echo -e "${YELLOW}qemu-nbd not found. Install qemu-base to enable hosts file injection.${NC}"
|
||||
return 1
|
||||
fi
|
||||
# Check for qemu-nbd
|
||||
if ! command -v qemu-nbd >/dev/null 2>&1; then
|
||||
echo -e "${YELLOW}qemu-nbd not found. Install qemu-base to enable hosts file injection.${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}Injecting /etc/hosts into disk image for VM: ${vm_name}${NC}"
|
||||
echo -e "${BLUE}Injecting /etc/hosts into disk image for VM: ${vm_name}${NC}"
|
||||
|
||||
# Load nbd module if needed
|
||||
if [[ ! -e /dev/nbd0 ]]; then
|
||||
modprobe nbd max_part=8 2>/dev/null || {
|
||||
echo -e "${YELLOW}Could not load nbd kernel module${NC}"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
# Load nbd module if needed
|
||||
if [[ ! -e /dev/nbd0 ]]; then
|
||||
modprobe nbd max_part=8 2>/dev/null || {
|
||||
echo -e "${YELLOW}Could not load nbd kernel module${NC}"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
# Find a free nbd device
|
||||
local nbd_dev=""
|
||||
for dev in /dev/nbd{0..15}; do
|
||||
if [[ -e $dev ]] && ! lsblk "$dev" > /dev/null 2>&1; then
|
||||
nbd_dev="$dev"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# Fallback: try /dev/nbd0 if no device was found via lsblk check
|
||||
if [[ -z $nbd_dev ]]; then
|
||||
nbd_dev="/dev/nbd0"
|
||||
fi
|
||||
# Find a free nbd device
|
||||
local nbd_dev=""
|
||||
for dev in /dev/nbd{0..15}; do
|
||||
if [[ -e $dev ]] && ! lsblk "$dev" >/dev/null 2>&1; then
|
||||
nbd_dev="$dev"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# Fallback: try /dev/nbd0 if no device was found via lsblk check
|
||||
if [[ -z $nbd_dev ]]; then
|
||||
nbd_dev="/dev/nbd0"
|
||||
fi
|
||||
|
||||
local mount_point="/tmp/vbox_hosts_inject_$$"
|
||||
local mount_point="/tmp/vbox_hosts_inject_$$"
|
||||
|
||||
# Connect disk image
|
||||
qemu-nbd --connect="$nbd_dev" "$disk_path" 2>/dev/null || {
|
||||
echo -e "${YELLOW}Could not connect disk image via qemu-nbd${NC}"
|
||||
return 1
|
||||
}
|
||||
# Connect disk image
|
||||
qemu-nbd --connect="$nbd_dev" "$disk_path" 2>/dev/null || {
|
||||
echo -e "${YELLOW}Could not connect disk image via qemu-nbd${NC}"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Wait for partitions to appear
|
||||
sleep 1
|
||||
partprobe "$nbd_dev" 2>/dev/null || true
|
||||
# Wait for partitions to appear
|
||||
sleep 1
|
||||
partprobe "$nbd_dev" 2>/dev/null || true
|
||||
|
||||
# Find the root partition (first Linux partition)
|
||||
local part=""
|
||||
for p in "${nbd_dev}p1" "${nbd_dev}p2" "${nbd_dev}p3"; do
|
||||
if [[ -b $p ]]; then
|
||||
part="$p"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# Find the root partition (first Linux partition)
|
||||
local part=""
|
||||
for p in "${nbd_dev}p1" "${nbd_dev}p2" "${nbd_dev}p3"; do
|
||||
if [[ -b $p ]]; then
|
||||
part="$p"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z $part ]]; then
|
||||
echo -e "${YELLOW}No partitions found on disk image${NC}"
|
||||
qemu-nbd --disconnect "$nbd_dev" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
if [[ -z $part ]]; then
|
||||
echo -e "${YELLOW}No partitions found on disk image${NC}"
|
||||
qemu-nbd --disconnect "$nbd_dev" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Mount the partition
|
||||
mkdir -p "$mount_point"
|
||||
if ! mount "$part" "$mount_point" 2>/dev/null; then
|
||||
# Journal may need recovery — run e2fsck then retry
|
||||
e2fsck -y "$part" > /dev/null 2>&1 || true
|
||||
if ! mount "$part" "$mount_point" 2>/dev/null; then
|
||||
echo -e "${YELLOW}Could not mount partition $part${NC}"
|
||||
qemu-nbd --disconnect "$nbd_dev" 2>/dev/null || true
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
# Mount the partition
|
||||
mkdir -p "$mount_point"
|
||||
if ! mount "$part" "$mount_point" 2>/dev/null; then
|
||||
# Journal may need recovery — run e2fsck then retry
|
||||
e2fsck -y "$part" >/dev/null 2>&1 || true
|
||||
if ! mount "$part" "$mount_point" 2>/dev/null; then
|
||||
echo -e "${YELLOW}Could not mount partition $part${NC}"
|
||||
qemu-nbd --disconnect "$nbd_dev" 2>/dev/null || true
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if this partition has /etc/hosts (i.e., it's the root fs)
|
||||
if [[ ! -f "$mount_point/etc/hosts" ]]; then
|
||||
echo -e "${YELLOW}Partition does not appear to be root filesystem (no /etc/hosts)${NC}"
|
||||
umount "$mount_point" 2>/dev/null || true
|
||||
qemu-nbd --disconnect "$nbd_dev" 2>/dev/null || true
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
# Check if this partition has /etc/hosts (i.e., it's the root fs)
|
||||
if [[ ! -f "$mount_point/etc/hosts" ]]; then
|
||||
echo -e "${YELLOW}Partition does not appear to be root filesystem (no /etc/hosts)${NC}"
|
||||
umount "$mount_point" 2>/dev/null || true
|
||||
qemu-nbd --disconnect "$nbd_dev" 2>/dev/null || true
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Backup original if not already backed up
|
||||
if [[ ! -f "$mount_point/etc/hosts.original" ]]; then
|
||||
cp "$mount_point/etc/hosts" "$mount_point/etc/hosts.original"
|
||||
echo -e "${CYAN}Backed up original hosts file${NC}"
|
||||
fi
|
||||
# Backup original if not already backed up
|
||||
if [[ ! -f "$mount_point/etc/hosts.original" ]]; then
|
||||
cp "$mount_point/etc/hosts" "$mount_point/etc/hosts.original"
|
||||
echo -e "${CYAN}Backed up original hosts file${NC}"
|
||||
fi
|
||||
|
||||
# Copy host's /etc/hosts into VM
|
||||
cp /etc/hosts "$mount_point/etc/hosts"
|
||||
chmod 444 "$mount_point/etc/hosts"
|
||||
# Copy host's /etc/hosts into VM
|
||||
cp /etc/hosts "$mount_point/etc/hosts"
|
||||
chmod 444 "$mount_point/etc/hosts"
|
||||
|
||||
local blocked_count
|
||||
blocked_count="$(grep -c '0.0.0.0' "$mount_point/etc/hosts")"
|
||||
local blocked_count
|
||||
blocked_count="$(grep -c '0.0.0.0' "$mount_point/etc/hosts")"
|
||||
|
||||
# Cleanup: unmount and disconnect
|
||||
umount "$mount_point" 2>/dev/null || true
|
||||
qemu-nbd --disconnect "$nbd_dev" 2>/dev/null || true
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
# Cleanup: unmount and disconnect
|
||||
umount "$mount_point" 2>/dev/null || true
|
||||
qemu-nbd --disconnect "$nbd_dev" 2>/dev/null || true
|
||||
rmdir "$mount_point" 2>/dev/null || true
|
||||
|
||||
echo -e "${GREEN}Hosts file injected into VM '${vm_name}' (${blocked_count} domains blocked)${NC}"
|
||||
return 0
|
||||
echo -e "${GREEN}Hosts file injected into VM '${vm_name}' (${blocked_count} domains blocked)${NC}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Apply enforcement to all VMs
|
||||
enforce_all_vms() {
|
||||
local -a vms
|
||||
mapfile -t vms < <(get_all_vms)
|
||||
local -a vms
|
||||
mapfile -t vms < <(get_all_vms)
|
||||
|
||||
if [[ ${#vms[@]} -eq 0 ]]; then
|
||||
echo -e "${YELLOW}No VirtualBox VMs found.${NC}"
|
||||
return 0
|
||||
fi
|
||||
if [[ ${#vms[@]} -eq 0 ]]; then
|
||||
echo -e "${YELLOW}No VirtualBox VMs found.${NC}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}Found ${#vms[@]} VM(s). Applying /etc/hosts enforcement...${NC}"
|
||||
echo -e "${CYAN}Found ${#vms[@]} VM(s). Applying /etc/hosts enforcement...${NC}"
|
||||
|
||||
local success=0
|
||||
local failed=0
|
||||
local success=0
|
||||
local failed=0
|
||||
|
||||
for vm in "${vms[@]}"; do
|
||||
echo -e "\n${BLUE}Processing VM: ${vm}${NC}"
|
||||
for vm in "${vms[@]}"; do
|
||||
echo -e "\n${BLUE}Processing VM: ${vm}${NC}"
|
||||
|
||||
# Configure DNS settings (works even when VM is running)
|
||||
configure_vm_dns "$vm"
|
||||
# Configure DNS settings (works even when VM is running)
|
||||
configure_vm_dns "$vm"
|
||||
|
||||
# Try to configure shared folder (only works when VM is stopped)
|
||||
if configure_hosts_shared_folder "$vm"; then
|
||||
((++success))
|
||||
else
|
||||
((++failed))
|
||||
echo -e "${YELLOW}Note: Stop the VM and run this script again to add shared folder${NC}"
|
||||
fi
|
||||
# Try to configure shared folder (only works when VM is stopped)
|
||||
if configure_hosts_shared_folder "$vm"; then
|
||||
((++success))
|
||||
else
|
||||
((++failed))
|
||||
echo -e "${YELLOW}Note: Stop the VM and run this script again to add shared folder${NC}"
|
||||
fi
|
||||
|
||||
# Inject hosts file directly into VM disk (the actual enforcement)
|
||||
inject_hosts_into_vm_disk "$vm" || true
|
||||
done
|
||||
# Inject hosts file directly into VM disk (the actual enforcement)
|
||||
inject_hosts_into_vm_disk "$vm" || true
|
||||
done
|
||||
|
||||
echo -e "\n${GREEN}Enforcement complete!${NC}"
|
||||
echo -e "Successfully configured: ${success} VM(s)"
|
||||
[[ $failed -gt 0 ]] && echo -e "${YELLOW}Needs VM shutdown for full config: ${failed} VM(s)${NC}"
|
||||
echo -e "\n${GREEN}Enforcement complete!${NC}"
|
||||
echo -e "Successfully configured: ${success} VM(s)"
|
||||
[[ $failed -gt 0 ]] && echo -e "${YELLOW}Needs VM shutdown for full config: ${failed} VM(s)${NC}"
|
||||
|
||||
# Mark that enforcement has been applied
|
||||
touch "$HOSTS_ENFORCEMENT_MARKER"
|
||||
# Mark that enforcement has been applied
|
||||
touch "$HOSTS_ENFORCEMENT_MARKER"
|
||||
}
|
||||
|
||||
# Check if a single VM has the shared folder configured
|
||||
vm_has_shared_folder() {
|
||||
local vm_name="$1"
|
||||
vboxmanage_as_user showvminfo "$vm_name" --machinereadable 2>/dev/null \
|
||||
| grep -q "SharedFolderNameMachineMapping.*=\"${VBOX_SHARED_FOLDER_NAME}\""
|
||||
local vm_name="$1"
|
||||
vboxmanage_as_user showvminfo "$vm_name" --machinereadable 2>/dev/null |
|
||||
grep -q "SharedFolderNameMachineMapping.*=\"${VBOX_SHARED_FOLDER_NAME}\""
|
||||
}
|
||||
|
||||
# Check if enforcement is applied to ALL registered VMs
|
||||
check_enforcement_status() {
|
||||
local -a vms
|
||||
mapfile -t vms < <(get_all_vms)
|
||||
local -a vms
|
||||
mapfile -t vms < <(get_all_vms)
|
||||
|
||||
if [[ ${#vms[@]} -eq 0 ]]; then
|
||||
echo -e "${GREEN}No VMs to enforce.${NC}"
|
||||
return 0
|
||||
fi
|
||||
if [[ ${#vms[@]} -eq 0 ]]; then
|
||||
echo -e "${GREEN}No VMs to enforce.${NC}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
for vm in "${vms[@]}"; do
|
||||
if ! vm_has_shared_folder "$vm"; then
|
||||
echo -e "${YELLOW}VM '${vm}' is missing hosts enforcement.${NC}"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
for vm in "${vms[@]}"; do
|
||||
if ! vm_has_shared_folder "$vm"; then
|
||||
echo -e "${YELLOW}VM '${vm}' is missing hosts enforcement.${NC}"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}All ${#vms[@]} VM(s) have hosts enforcement applied.${NC}"
|
||||
return 0
|
||||
echo -e "${GREEN}All ${#vms[@]} VM(s) have hosts enforcement applied.${NC}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Show status
|
||||
show_status() {
|
||||
echo -e "${CYAN}VirtualBox Hosts Enforcement Status${NC}"
|
||||
echo -e "${CYAN}====================================${NC}\n"
|
||||
echo -e "${CYAN}VirtualBox Hosts Enforcement Status${NC}"
|
||||
echo -e "${CYAN}====================================${NC}\n"
|
||||
|
||||
local -a all_vms running_vms
|
||||
mapfile -t all_vms < <(get_all_vms)
|
||||
mapfile -t running_vms < <(get_running_vms)
|
||||
local -a all_vms running_vms
|
||||
mapfile -t all_vms < <(get_all_vms)
|
||||
mapfile -t running_vms < <(get_running_vms)
|
||||
|
||||
echo -e "Total VMs: ${#all_vms[@]}"
|
||||
echo -e "Running VMs: ${#running_vms[@]}"
|
||||
echo -e "Total VMs: ${#all_vms[@]}"
|
||||
echo -e "Running VMs: ${#running_vms[@]}"
|
||||
|
||||
if check_enforcement_status > /dev/null 2>&1; then
|
||||
echo -e "Enforcement status: ${GREEN}Applied to all VMs${NC}"
|
||||
else
|
||||
echo -e "Enforcement status: ${RED}Not fully applied${NC}"
|
||||
fi
|
||||
if check_enforcement_status >/dev/null 2>&1; then
|
||||
echo -e "Enforcement status: ${GREEN}Applied to all VMs${NC}"
|
||||
else
|
||||
echo -e "Enforcement status: ${RED}Not fully applied${NC}"
|
||||
fi
|
||||
|
||||
echo -e "\n${CYAN}VMs:${NC}"
|
||||
for vm in "${all_vms[@]}"; do
|
||||
local flags=""
|
||||
if printf '%s\n' "${running_vms[@]}" | grep -qx "$vm"; then
|
||||
flags+=" ${GREEN}[RUNNING]${NC}"
|
||||
fi
|
||||
if vm_has_shared_folder "$vm"; then
|
||||
flags+=" ${GREEN}[ENFORCED]${NC}"
|
||||
else
|
||||
flags+=" ${RED}[NOT ENFORCED]${NC}"
|
||||
fi
|
||||
echo -e " - ${vm}${flags}"
|
||||
done
|
||||
echo -e "\n${CYAN}VMs:${NC}"
|
||||
for vm in "${all_vms[@]}"; do
|
||||
local flags=""
|
||||
if printf '%s\n' "${running_vms[@]}" | grep -qx "$vm"; then
|
||||
flags+=" ${GREEN}[RUNNING]${NC}"
|
||||
fi
|
||||
if vm_has_shared_folder "$vm"; then
|
||||
flags+=" ${GREEN}[ENFORCED]${NC}"
|
||||
else
|
||||
flags+=" ${RED}[NOT ENFORCED]${NC}"
|
||||
fi
|
||||
echo -e " - ${vm}${flags}"
|
||||
done
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local action="${1:-enforce}"
|
||||
local action="${1:-enforce}"
|
||||
|
||||
case "$action" in
|
||||
enforce|apply)
|
||||
enforce_all_vms
|
||||
;;
|
||||
check)
|
||||
if check_enforcement_status; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
generate-script)
|
||||
local output="${2:-/tmp/vbox_hosts_sync.sh}"
|
||||
generate_vm_startup_script "$output"
|
||||
;;
|
||||
*)
|
||||
echo -e "${CYAN}VirtualBox /etc/hosts Enforcement Tool${NC}"
|
||||
echo ""
|
||||
echo "Usage: $0 [command]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " enforce Apply /etc/hosts enforcement to all VMs (default)"
|
||||
echo " check Check if enforcement has been applied"
|
||||
echo " status Show current enforcement status"
|
||||
echo " generate-script [path] Generate a script to place in VMs for hosts sync"
|
||||
echo ""
|
||||
echo "This tool configures VirtualBox VMs to:"
|
||||
echo " 1. Use host's DNS resolution (via NAT DNS proxy)"
|
||||
echo " 2. Share /etc from host (read-only) for hosts file access"
|
||||
echo ""
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
case "$action" in
|
||||
enforce | apply)
|
||||
enforce_all_vms
|
||||
;;
|
||||
check)
|
||||
if check_enforcement_status; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
generate-script)
|
||||
local output="${2:-/tmp/vbox_hosts_sync.sh}"
|
||||
generate_vm_startup_script "$output"
|
||||
;;
|
||||
*)
|
||||
echo -e "${CYAN}VirtualBox /etc/hosts Enforcement Tool${NC}"
|
||||
echo ""
|
||||
echo "Usage: $0 [command]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " enforce Apply /etc/hosts enforcement to all VMs (default)"
|
||||
echo " check Check if enforcement has been applied"
|
||||
echo " status Show current enforcement status"
|
||||
echo " generate-script [path] Generate a script to place in VMs for hosts sync"
|
||||
echo ""
|
||||
echo "This tool configures VirtualBox VMs to:"
|
||||
echo " 1. Use host's DNS resolution (via NAT DNS proxy)"
|
||||
echo " 2. Share /etc from host (read-only) for hosts file access"
|
||||
echo ""
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@ -15,7 +15,7 @@ declare -a FINDINGS=()
|
||||
declare -a ACTIONS=()
|
||||
|
||||
usage() {
|
||||
cat << 'EOF'
|
||||
cat <<'EOF'
|
||||
diagnose_arch_performance.sh - Diagnose common causes of Arch Linux slowness/instability
|
||||
|
||||
Usage:
|
||||
@ -38,334 +38,334 @@ EOF
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--apply-safe-fixes)
|
||||
APPLY_SAFE_FIXES=true
|
||||
shift
|
||||
;;
|
||||
--install-tools)
|
||||
INSTALL_TOOLS=true
|
||||
shift
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--apply-safe-fixes)
|
||||
APPLY_SAFE_FIXES=true
|
||||
shift
|
||||
;;
|
||||
--install-tools)
|
||||
INSTALL_TOOLS=true
|
||||
shift
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
add_finding() {
|
||||
FINDINGS+=("$1")
|
||||
log_warn "$1"
|
||||
FINDINGS+=("$1")
|
||||
log_warn "$1"
|
||||
}
|
||||
|
||||
add_action() {
|
||||
ACTIONS+=("$1")
|
||||
log_info "$1"
|
||||
ACTIONS+=("$1")
|
||||
log_info "$1"
|
||||
}
|
||||
|
||||
run_and_log() {
|
||||
local header="$1"
|
||||
shift
|
||||
{
|
||||
echo
|
||||
echo "=== $header ==="
|
||||
"$@" 2>&1 || true
|
||||
} >> "$REPORT_FILE"
|
||||
local header="$1"
|
||||
shift
|
||||
{
|
||||
echo
|
||||
echo "=== $header ==="
|
||||
"$@" 2>&1 || true
|
||||
} >>"$REPORT_FILE"
|
||||
}
|
||||
|
||||
check_root_if_needed() {
|
||||
if [[ $APPLY_SAFE_FIXES == "true" || $INSTALL_TOOLS == "true" ]]; then
|
||||
require_root "$@"
|
||||
fi
|
||||
if [[ $APPLY_SAFE_FIXES == "true" || $INSTALL_TOOLS == "true" ]]; then
|
||||
require_root "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
install_optional_tools() {
|
||||
if [[ $INSTALL_TOOLS != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
if [[ $INSTALL_TOOLS != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local packages=(lm_sensors smartmontools nvtop iotop powertop)
|
||||
log_info "Installing optional diagnostic packages: ${packages[*]}"
|
||||
pacman -S --needed --noconfirm "${packages[@]}"
|
||||
local packages=(lm_sensors smartmontools nvtop iotop powertop)
|
||||
log_info "Installing optional diagnostic packages: ${packages[*]}"
|
||||
pacman -S --needed --noconfirm "${packages[@]}"
|
||||
}
|
||||
|
||||
collect_basics() {
|
||||
run_and_log "Kernel" uname -a
|
||||
run_and_log "Uptime" uptime
|
||||
run_and_log "Memory" free -h
|
||||
run_and_log "Swap" swapon --show
|
||||
run_and_log "CPU (lscpu)" lscpu
|
||||
run_and_log "Disk Usage" df -h /
|
||||
run_and_log "Boot Time" systemd-analyze
|
||||
run_and_log "Failed Units" systemctl --failed --no-pager
|
||||
run_and_log "Recent Errors (this boot)" journalctl -b -p err --no-pager -n 200
|
||||
run_and_log "Kernel" uname -a
|
||||
run_and_log "Uptime" uptime
|
||||
run_and_log "Memory" free -h
|
||||
run_and_log "Swap" swapon --show
|
||||
run_and_log "CPU (lscpu)" lscpu
|
||||
run_and_log "Disk Usage" df -h /
|
||||
run_and_log "Boot Time" systemd-analyze
|
||||
run_and_log "Failed Units" systemctl --failed --no-pager
|
||||
run_and_log "Recent Errors (this boot)" journalctl -b -p err --no-pager -n 200
|
||||
|
||||
local cpu_count
|
||||
cpu_count=$(getconf _NPROCESSORS_ONLN 2> /dev/null || echo 1)
|
||||
local load1
|
||||
load1=$(awk '{print int($1)}' /proc/loadavg 2> /dev/null || echo 0)
|
||||
if [[ ${load1:-0} -ge ${cpu_count:-1} ]]; then
|
||||
add_finding "1-minute load average is at/above CPU thread count (${load1}/${cpu_count}); background tasks may be saturating the system."
|
||||
fi
|
||||
local cpu_count
|
||||
cpu_count=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
|
||||
local load1
|
||||
load1=$(awk '{print int($1)}' /proc/loadavg 2>/dev/null || echo 0)
|
||||
if [[ ${load1:-0} -ge ${cpu_count:-1} ]]; then
|
||||
add_finding "1-minute load average is at/above CPU thread count (${load1}/${cpu_count}); background tasks may be saturating the system."
|
||||
fi
|
||||
|
||||
local failed_count
|
||||
failed_count=$(systemctl --failed --no-legend 2> /dev/null | wc -l || true)
|
||||
failed_count=${failed_count//[[:space:]]/}
|
||||
if [[ ${failed_count:-0} -gt 0 ]]; then
|
||||
add_finding "One or more systemd units are failed (${failed_count}); failed services can cause repeated retries and instability."
|
||||
fi
|
||||
local failed_count
|
||||
failed_count=$(systemctl --failed --no-legend 2>/dev/null | wc -l || true)
|
||||
failed_count=${failed_count//[[:space:]]/}
|
||||
if [[ ${failed_count:-0} -gt 0 ]]; then
|
||||
add_finding "One or more systemd units are failed (${failed_count}); failed services can cause repeated retries and instability."
|
||||
fi
|
||||
|
||||
local acpi_error_count
|
||||
acpi_error_count=$(journalctl -b -p err --no-pager 2> /dev/null | grep -ic 'acpi' || true)
|
||||
if [[ ${acpi_error_count:-0} -ge 5 ]]; then
|
||||
add_finding "Frequent ACPI errors detected in current boot (${acpi_error_count}); BIOS/firmware update may improve stability."
|
||||
fi
|
||||
local acpi_error_count
|
||||
acpi_error_count=$(journalctl -b -p err --no-pager 2>/dev/null | grep -ic 'acpi' || true)
|
||||
if [[ ${acpi_error_count:-0} -ge 5 ]]; then
|
||||
add_finding "Frequent ACPI errors detected in current boot (${acpi_error_count}); BIOS/firmware update may improve stability."
|
||||
fi
|
||||
|
||||
local top_snapshot
|
||||
top_snapshot=$(ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head -n 12 || true)
|
||||
{
|
||||
echo
|
||||
echo "=== Top CPU Processes ==="
|
||||
echo "$top_snapshot"
|
||||
} >> "$REPORT_FILE"
|
||||
local top_snapshot
|
||||
top_snapshot=$(ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head -n 12 || true)
|
||||
{
|
||||
echo
|
||||
echo "=== Top CPU Processes ==="
|
||||
echo "$top_snapshot"
|
||||
} >>"$REPORT_FILE"
|
||||
|
||||
local xorg_cpu
|
||||
xorg_cpu=$(ps -C Xorg -o %cpu= | awk '{sum+=$1} END {printf "%.0f", sum+0}' || echo 0)
|
||||
if [[ ${xorg_cpu:-0} -ge 20 ]]; then
|
||||
add_finding "Xorg is using high CPU (${xorg_cpu}%); desktop/compositor/GPU driver path may be a primary slowdown source."
|
||||
fi
|
||||
local xorg_cpu
|
||||
xorg_cpu=$(ps -C Xorg -o %cpu= | awk '{sum+=$1} END {printf "%.0f", sum+0}' || echo 0)
|
||||
if [[ ${xorg_cpu:-0} -ge 20 ]]; then
|
||||
add_finding "Xorg is using high CPU (${xorg_cpu}%); desktop/compositor/GPU driver path may be a primary slowdown source."
|
||||
fi
|
||||
}
|
||||
|
||||
check_cpu_governor() {
|
||||
local gov_files
|
||||
gov_files=$(find /sys/devices/system/cpu -maxdepth 3 -name scaling_governor 2> /dev/null || true)
|
||||
local gov_files
|
||||
gov_files=$(find /sys/devices/system/cpu -maxdepth 3 -name scaling_governor 2>/dev/null || true)
|
||||
|
||||
if [[ -z $gov_files ]]; then
|
||||
add_action "CPU governor files not found (may be unsupported on this platform)."
|
||||
return
|
||||
fi
|
||||
if [[ -z $gov_files ]]; then
|
||||
add_action "CPU governor files not found (may be unsupported on this platform)."
|
||||
return
|
||||
fi
|
||||
|
||||
local summary
|
||||
summary=$(awk '{count[$1]++} END {for (g in count) printf "%s:%d ", g, count[g]}' $gov_files 2> /dev/null || true)
|
||||
echo "CPU governor summary: ${summary:-unknown}" >> "$REPORT_FILE"
|
||||
local summary
|
||||
summary=$(awk '{count[$1]++} END {for (g in count) printf "%s:%d ", g, count[g]}' $gov_files 2>/dev/null || true)
|
||||
echo "CPU governor summary: ${summary:-unknown}" >>"$REPORT_FILE"
|
||||
|
||||
if grep -q '^powersave$' $gov_files 2> /dev/null; then
|
||||
add_finding "CPU governor includes 'powersave' on one or more cores; this can make high-end hardware feel slow."
|
||||
fi
|
||||
if grep -q '^powersave$' $gov_files 2>/dev/null; then
|
||||
add_finding "CPU governor includes 'powersave' on one or more cores; this can make high-end hardware feel slow."
|
||||
fi
|
||||
}
|
||||
|
||||
check_thermal_state() {
|
||||
if has_cmd sensors; then
|
||||
run_and_log "Temperatures (sensors)" sensors
|
||||
else
|
||||
add_action "Install lm_sensors and run 'sensors' to verify thermal throttling."
|
||||
fi
|
||||
if has_cmd sensors; then
|
||||
run_and_log "Temperatures (sensors)" sensors
|
||||
else
|
||||
add_action "Install lm_sensors and run 'sensors' to verify thermal throttling."
|
||||
fi
|
||||
|
||||
if has_cmd dmesg; then
|
||||
local therm_hits
|
||||
therm_hits=$(dmesg | grep -Ei 'throttl|thermal|overheat|cpu clock throttled' | tail -n 30 || true)
|
||||
if [[ -n $therm_hits ]]; then
|
||||
add_finding "Kernel logs show thermal/throttling related messages."
|
||||
{
|
||||
echo
|
||||
echo "=== Thermal/Throttling dmesg excerpts ==="
|
||||
echo "$therm_hits"
|
||||
} >> "$REPORT_FILE"
|
||||
fi
|
||||
fi
|
||||
if has_cmd dmesg; then
|
||||
local therm_hits
|
||||
therm_hits=$(dmesg | grep -Ei 'throttl|thermal|overheat|cpu clock throttled' | tail -n 30 || true)
|
||||
if [[ -n $therm_hits ]]; then
|
||||
add_finding "Kernel logs show thermal/throttling related messages."
|
||||
{
|
||||
echo
|
||||
echo "=== Thermal/Throttling dmesg excerpts ==="
|
||||
echo "$therm_hits"
|
||||
} >>"$REPORT_FILE"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_power_services() {
|
||||
local tlp_enabled="false"
|
||||
local ppd_enabled="false"
|
||||
local tlp_enabled="false"
|
||||
local ppd_enabled="false"
|
||||
|
||||
if systemctl is-enabled tlp.service > /dev/null 2>&1; then
|
||||
tlp_enabled="true"
|
||||
fi
|
||||
if systemctl is-enabled power-profiles-daemon.service > /dev/null 2>&1; then
|
||||
ppd_enabled="true"
|
||||
fi
|
||||
if systemctl is-enabled tlp.service >/dev/null 2>&1; then
|
||||
tlp_enabled="true"
|
||||
fi
|
||||
if systemctl is-enabled power-profiles-daemon.service >/dev/null 2>&1; then
|
||||
ppd_enabled="true"
|
||||
fi
|
||||
|
||||
echo "Power services: tlp=${tlp_enabled}, power-profiles-daemon=${ppd_enabled}" >> "$REPORT_FILE"
|
||||
echo "Power services: tlp=${tlp_enabled}, power-profiles-daemon=${ppd_enabled}" >>"$REPORT_FILE"
|
||||
|
||||
if [[ $tlp_enabled == "true" && $ppd_enabled == "true" ]]; then
|
||||
add_finding "Both TLP and power-profiles-daemon are enabled; they often conflict and cause inconsistent performance."
|
||||
fi
|
||||
if [[ $tlp_enabled == "true" && $ppd_enabled == "true" ]]; then
|
||||
add_finding "Both TLP and power-profiles-daemon are enabled; they often conflict and cause inconsistent performance."
|
||||
fi
|
||||
|
||||
if [[ $tlp_enabled == "false" && $ppd_enabled == "false" ]]; then
|
||||
add_action "No power management daemon is enabled; consider installing/enabling power-profiles-daemon for predictable AC/battery behavior."
|
||||
fi
|
||||
if [[ $tlp_enabled == "false" && $ppd_enabled == "false" ]]; then
|
||||
add_action "No power management daemon is enabled; consider installing/enabling power-profiles-daemon for predictable AC/battery behavior."
|
||||
fi
|
||||
}
|
||||
|
||||
check_storage_health() {
|
||||
run_and_log "Block Devices" lsblk -o NAME,MODEL,ROTA,SIZE,TYPE,MOUNTPOINT,FSTYPE
|
||||
run_and_log "Block Devices" lsblk -o NAME,MODEL,ROTA,SIZE,TYPE,MOUNTPOINT,FSTYPE
|
||||
|
||||
if has_cmd fstrim; then
|
||||
run_and_log "fstrim dry-run" fstrim -av --dry-run
|
||||
fi
|
||||
if has_cmd fstrim; then
|
||||
run_and_log "fstrim dry-run" fstrim -av --dry-run
|
||||
fi
|
||||
|
||||
if systemctl is-enabled fstrim.timer > /dev/null 2>&1; then
|
||||
add_action "fstrim.timer is enabled (good for SSD performance longevity)."
|
||||
else
|
||||
add_finding "fstrim.timer is not enabled; SSD maintenance trimming may be missing."
|
||||
fi
|
||||
if systemctl is-enabled fstrim.timer >/dev/null 2>&1; then
|
||||
add_action "fstrim.timer is enabled (good for SSD performance longevity)."
|
||||
else
|
||||
add_finding "fstrim.timer is not enabled; SSD maintenance trimming may be missing."
|
||||
fi
|
||||
|
||||
if has_cmd smartctl; then
|
||||
local root_disk
|
||||
root_disk=$(findmnt -n -o SOURCE / | sed 's/[0-9]*$//' | sed 's/p$//' || true)
|
||||
if [[ -n ${root_disk:-} && -b $root_disk ]]; then
|
||||
run_and_log "SMART Summary ($root_disk)" smartctl -H "$root_disk"
|
||||
fi
|
||||
else
|
||||
add_action "Install smartmontools and run SMART health checks for your SSD/NVMe."
|
||||
fi
|
||||
if has_cmd smartctl; then
|
||||
local root_disk
|
||||
root_disk=$(findmnt -n -o SOURCE / | sed 's/[0-9]*$//' | sed 's/p$//' || true)
|
||||
if [[ -n ${root_disk:-} && -b $root_disk ]]; then
|
||||
run_and_log "SMART Summary ($root_disk)" smartctl -H "$root_disk"
|
||||
fi
|
||||
else
|
||||
add_action "Install smartmontools and run SMART health checks for your SSD/NVMe."
|
||||
fi
|
||||
}
|
||||
|
||||
check_memory_pressure() {
|
||||
local mem_total mem_available swap_total swap_free
|
||||
mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
|
||||
mem_available=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
|
||||
swap_total=$(awk '/SwapTotal/ {print $2}' /proc/meminfo)
|
||||
swap_free=$(awk '/SwapFree/ {print $2}' /proc/meminfo)
|
||||
local mem_total mem_available swap_total swap_free
|
||||
mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
|
||||
mem_available=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
|
||||
swap_total=$(awk '/SwapTotal/ {print $2}' /proc/meminfo)
|
||||
swap_free=$(awk '/SwapFree/ {print $2}' /proc/meminfo)
|
||||
|
||||
if [[ ${swap_total:-0} -gt 0 ]]; then
|
||||
local swap_used
|
||||
swap_used=$((swap_total - swap_free))
|
||||
local swap_pct
|
||||
swap_pct=$((swap_used * 100 / swap_total))
|
||||
echo "Swap usage: ${swap_pct}%" >> "$REPORT_FILE"
|
||||
if [[ $swap_pct -ge 35 && ${mem_available:-0} -gt $((mem_total / 3)) ]]; then
|
||||
add_finding "High swap usage while RAM is still available; this can cause stutter."
|
||||
add_action "Consider lowering swappiness (temporary: sudo sysctl vm.swappiness=10)."
|
||||
fi
|
||||
fi
|
||||
if [[ ${swap_total:-0} -gt 0 ]]; then
|
||||
local swap_used
|
||||
swap_used=$((swap_total - swap_free))
|
||||
local swap_pct
|
||||
swap_pct=$((swap_used * 100 / swap_total))
|
||||
echo "Swap usage: ${swap_pct}%" >>"$REPORT_FILE"
|
||||
if [[ $swap_pct -ge 35 && ${mem_available:-0} -gt $((mem_total / 3)) ]]; then
|
||||
add_finding "High swap usage while RAM is still available; this can cause stutter."
|
||||
add_action "Consider lowering swappiness (temporary: sudo sysctl vm.swappiness=10)."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f /proc/pressure/memory ]]; then
|
||||
run_and_log "Memory PSI" cat /proc/pressure/memory
|
||||
fi
|
||||
if [[ -f /proc/pressure/memory ]]; then
|
||||
run_and_log "Memory PSI" cat /proc/pressure/memory
|
||||
fi
|
||||
}
|
||||
|
||||
check_gpu_state() {
|
||||
if has_cmd nvidia-smi; then
|
||||
run_and_log "NVIDIA State" nvidia-smi
|
||||
local pstate util power
|
||||
pstate=$(nvidia-smi --query-gpu=pstate --format=csv,noheader 2> /dev/null | head -n 1 | xargs || true)
|
||||
util=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2> /dev/null | head -n 1 | xargs || true)
|
||||
power=$(nvidia-smi --query-gpu=power.draw --format=csv,noheader,nounits 2> /dev/null | head -n 1 | xargs || true)
|
||||
if has_cmd nvidia-smi; then
|
||||
run_and_log "NVIDIA State" nvidia-smi
|
||||
local pstate util power
|
||||
pstate=$(nvidia-smi --query-gpu=pstate --format=csv,noheader 2>/dev/null | head -n 1 | xargs || true)
|
||||
util=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null | head -n 1 | xargs || true)
|
||||
power=$(nvidia-smi --query-gpu=power.draw --format=csv,noheader,nounits 2>/dev/null | head -n 1 | xargs || true)
|
||||
|
||||
echo "NVIDIA pstate: ${pstate:-unknown}" >> "$REPORT_FILE"
|
||||
echo "NVIDIA util: ${util:-unknown}%" >> "$REPORT_FILE"
|
||||
echo "NVIDIA power: ${power:-unknown}W" >> "$REPORT_FILE"
|
||||
echo "NVIDIA pstate: ${pstate:-unknown}" >>"$REPORT_FILE"
|
||||
echo "NVIDIA util: ${util:-unknown}%" >>"$REPORT_FILE"
|
||||
echo "NVIDIA power: ${power:-unknown}W" >>"$REPORT_FILE"
|
||||
|
||||
if [[ ${pstate:-} == "P0" && ${util:-100} -le 5 ]]; then
|
||||
add_finding "NVIDIA GPU is in P0 high-performance state while mostly idle; this can increase heat and trigger thermal limits."
|
||||
add_action "If laptop has hybrid graphics, prefer iGPU mode for desktop workloads and use dGPU on demand."
|
||||
fi
|
||||
else
|
||||
run_and_log "PCI VGA Devices" lspci -nnk | grep -A3 -Ei 'vga|3d|display'
|
||||
fi
|
||||
if [[ ${pstate:-} == "P0" && ${util:-100} -le 5 ]]; then
|
||||
add_finding "NVIDIA GPU is in P0 high-performance state while mostly idle; this can increase heat and trigger thermal limits."
|
||||
add_action "If laptop has hybrid graphics, prefer iGPU mode for desktop workloads and use dGPU on demand."
|
||||
fi
|
||||
else
|
||||
run_and_log "PCI VGA Devices" lspci -nnk | grep -A3 -Ei 'vga|3d|display'
|
||||
fi
|
||||
}
|
||||
|
||||
check_journal_size() {
|
||||
local journal_line
|
||||
journal_line=$(journalctl --disk-usage 2> /dev/null || true)
|
||||
echo "Journal usage: ${journal_line:-unknown}" >> "$REPORT_FILE"
|
||||
local journal_line
|
||||
journal_line=$(journalctl --disk-usage 2>/dev/null || true)
|
||||
echo "Journal usage: ${journal_line:-unknown}" >>"$REPORT_FILE"
|
||||
|
||||
if [[ $journal_line =~ ([0-9]+\.?[0-9]*)\ (G|M) ]]; then
|
||||
local value unit
|
||||
value="${BASH_REMATCH[1]}"
|
||||
unit="${BASH_REMATCH[2]}"
|
||||
if [[ $unit == "G" ]]; then
|
||||
add_finding "Systemd journal is large (${value}G); excessive logs can waste I/O and disk space."
|
||||
fi
|
||||
fi
|
||||
if [[ $journal_line =~ ([0-9]+\.?[0-9]*)\ (G|M) ]]; then
|
||||
local value unit
|
||||
value="${BASH_REMATCH[1]}"
|
||||
unit="${BASH_REMATCH[2]}"
|
||||
if [[ $unit == "G" ]]; then
|
||||
add_finding "Systemd journal is large (${value}G); excessive logs can waste I/O and disk space."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
apply_safe_fixes() {
|
||||
if [[ $APPLY_SAFE_FIXES != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
if [[ $APPLY_SAFE_FIXES != "true" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
log_info "Applying safe fixes..."
|
||||
log_info "Applying safe fixes..."
|
||||
|
||||
if ! systemctl is-enabled fstrim.timer > /dev/null 2>&1; then
|
||||
systemctl enable --now fstrim.timer
|
||||
add_action "Enabled and started fstrim.timer."
|
||||
fi
|
||||
if ! systemctl is-enabled fstrim.timer >/dev/null 2>&1; then
|
||||
systemctl enable --now fstrim.timer
|
||||
add_action "Enabled and started fstrim.timer."
|
||||
fi
|
||||
|
||||
if systemctl is-enabled tlp.service > /dev/null 2>&1 && systemctl is-enabled power-profiles-daemon.service > /dev/null 2>&1; then
|
||||
systemctl disable --now tlp.service
|
||||
add_action "Disabled tlp.service to avoid conflict with power-profiles-daemon."
|
||||
fi
|
||||
if systemctl is-enabled tlp.service >/dev/null 2>&1 && systemctl is-enabled power-profiles-daemon.service >/dev/null 2>&1; then
|
||||
systemctl disable --now tlp.service
|
||||
add_action "Disabled tlp.service to avoid conflict with power-profiles-daemon."
|
||||
fi
|
||||
|
||||
local journal_line
|
||||
journal_line=$(journalctl --disk-usage 2> /dev/null || true)
|
||||
if [[ $journal_line =~ ([0-9]+\.?[0-9]*)\ G ]]; then
|
||||
journalctl --vacuum-size=300M
|
||||
add_action "Vacuumed systemd journal to 300M."
|
||||
fi
|
||||
local journal_line
|
||||
journal_line=$(journalctl --disk-usage 2>/dev/null || true)
|
||||
if [[ $journal_line =~ ([0-9]+\.?[0-9]*)\ G ]]; then
|
||||
journalctl --vacuum-size=300M
|
||||
add_action "Vacuumed systemd journal to 300M."
|
||||
fi
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
echo
|
||||
echo "=============================="
|
||||
echo " Arch Performance Diagnostics"
|
||||
echo "=============================="
|
||||
echo "Report: $REPORT_FILE"
|
||||
echo
|
||||
echo
|
||||
echo "=============================="
|
||||
echo " Arch Performance Diagnostics"
|
||||
echo "=============================="
|
||||
echo "Report: $REPORT_FILE"
|
||||
echo
|
||||
|
||||
if [[ ${#FINDINGS[@]} -eq 0 ]]; then
|
||||
log_ok "No high-confidence bottlenecks detected by automated checks."
|
||||
else
|
||||
log_warn "Likely issues found (${#FINDINGS[@]}):"
|
||||
local item
|
||||
for item in "${FINDINGS[@]}"; do
|
||||
echo " - $item"
|
||||
done
|
||||
fi
|
||||
if [[ ${#FINDINGS[@]} -eq 0 ]]; then
|
||||
log_ok "No high-confidence bottlenecks detected by automated checks."
|
||||
else
|
||||
log_warn "Likely issues found (${#FINDINGS[@]}):"
|
||||
local item
|
||||
for item in "${FINDINGS[@]}"; do
|
||||
echo " - $item"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ ${#ACTIONS[@]} -gt 0 ]]; then
|
||||
echo
|
||||
log_info "Actions/recommendations:"
|
||||
local action
|
||||
for action in "${ACTIONS[@]}"; do
|
||||
echo " - $action"
|
||||
done
|
||||
fi
|
||||
if [[ ${#ACTIONS[@]} -gt 0 ]]; then
|
||||
echo
|
||||
log_info "Actions/recommendations:"
|
||||
local action
|
||||
for action in "${ACTIONS[@]}"; do
|
||||
echo " - $action"
|
||||
done
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Recommended next command for deep per-process analysis:"
|
||||
echo " sudo iotop -oPa"
|
||||
echo " top"
|
||||
echo " systemd-analyze blame"
|
||||
echo
|
||||
echo "Recommended next command for deep per-process analysis:"
|
||||
echo " sudo iotop -oPa"
|
||||
echo " top"
|
||||
echo " systemd-analyze blame"
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
check_root_if_needed "$@"
|
||||
parse_args "$@"
|
||||
check_root_if_needed "$@"
|
||||
|
||||
mkdir -p "$REPORT_DIR"
|
||||
log_info "Writing diagnostic report to: $REPORT_FILE"
|
||||
mkdir -p "$REPORT_DIR"
|
||||
log_info "Writing diagnostic report to: $REPORT_FILE"
|
||||
|
||||
collect_basics
|
||||
install_optional_tools
|
||||
check_cpu_governor
|
||||
check_thermal_state
|
||||
check_power_services
|
||||
check_storage_health
|
||||
check_memory_pressure
|
||||
check_gpu_state
|
||||
check_journal_size
|
||||
apply_safe_fixes
|
||||
print_summary
|
||||
collect_basics
|
||||
install_optional_tools
|
||||
check_cpu_governor
|
||||
check_thermal_state
|
||||
check_power_services
|
||||
check_storage_health
|
||||
check_memory_pressure
|
||||
check_gpu_state
|
||||
check_journal_size
|
||||
apply_safe_fixes
|
||||
print_summary
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@ -30,12 +30,12 @@ shift "$COMMON_ARGS_SHIFT"
|
||||
|
||||
DRY_RUN=false
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
;;
|
||||
-h | --help)
|
||||
cat << 'EOF'
|
||||
case "$arg" in
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
;;
|
||||
-h | --help)
|
||||
cat <<'EOF'
|
||||
fix_arch_performance.sh - Fix common Arch Linux laptop performance issues
|
||||
|
||||
Usage: fix_arch_performance.sh [OPTIONS]
|
||||
@ -55,9 +55,9 @@ Fixes applied:
|
||||
All fixes are idempotent and safe to re-run.
|
||||
Xorg fixes require reboot/re-login to take effect.
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_root "$@"
|
||||
@ -71,53 +71,53 @@ FIXES_SKIPPED=0
|
||||
# Helper: run or print a fix depending on --dry-run / --interactive
|
||||
# ---------------------------------------------------------------------------
|
||||
apply_fix() {
|
||||
local description="$1"
|
||||
shift
|
||||
local description="$1"
|
||||
shift
|
||||
|
||||
echo ""
|
||||
log_info "$description"
|
||||
echo ""
|
||||
log_info "$description"
|
||||
|
||||
if [[ $DRY_RUN == "true" ]]; then
|
||||
echo " [dry-run] Would run: $*"
|
||||
return 0
|
||||
fi
|
||||
if [[ $DRY_RUN == "true" ]]; then
|
||||
echo " [dry-run] Would run: $*"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||
if ! ask_yes_no " Apply this fix?"; then
|
||||
log_warn "Skipped."
|
||||
((FIXES_SKIPPED++)) || true
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||
if ! ask_yes_no " Apply this fix?"; then
|
||||
log_warn "Skipped."
|
||||
((FIXES_SKIPPED++)) || true
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if "$@"; then
|
||||
log_ok "Done."
|
||||
((FIXES_APPLIED++)) || true
|
||||
else
|
||||
log_error "Failed (non-fatal, continuing)."
|
||||
fi
|
||||
if "$@"; then
|
||||
log_ok "Done."
|
||||
((FIXES_APPLIED++)) || true
|
||||
else
|
||||
log_error "Failed (non-fatal, continuing)."
|
||||
fi
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# Fix 1: NVIDIA RenderAccel
|
||||
# ===================================================================
|
||||
fix_nvidia_render_accel() {
|
||||
local conf="/etc/X11/xorg.conf.d/20-nvidia.conf"
|
||||
local conf="/etc/X11/xorg.conf.d/20-nvidia.conf"
|
||||
|
||||
# Check if RenderAccel is already correct
|
||||
if [[ -f $conf ]] && grep -qi 'RenderAccel.*true' "$conf"; then
|
||||
log_ok "NVIDIA RenderAccel is already enabled — skipping."
|
||||
return 0
|
||||
fi
|
||||
# Check if RenderAccel is already correct
|
||||
if [[ -f $conf ]] && grep -qi 'RenderAccel.*true' "$conf"; then
|
||||
log_ok "NVIDIA RenderAccel is already enabled — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
mkdir -p /etc/X11/xorg.conf.d
|
||||
mkdir -p /etc/X11/xorg.conf.d
|
||||
|
||||
# Back up the current config if it exists and has the bad setting
|
||||
if [[ -f $conf ]]; then
|
||||
cp "$conf" "${conf}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
fi
|
||||
# Back up the current config if it exists and has the bad setting
|
||||
if [[ -f $conf ]]; then
|
||||
cp "$conf" "${conf}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
fi
|
||||
|
||||
cat > "$conf" << 'XORGEOF'
|
||||
cat >"$conf" <<'XORGEOF'
|
||||
# NVIDIA configuration - hardware acceleration enabled
|
||||
# Disabling RenderAccel forces Xorg into software rendering,
|
||||
# causing 30%+ CPU usage on desktop. Keep this set to "true".
|
||||
@ -128,131 +128,131 @@ Section "Device"
|
||||
EndSection
|
||||
XORGEOF
|
||||
|
||||
# Clean up old backups left by nvidia_troubleshoot.sh
|
||||
rm -f /etc/X11/xorg.conf.d/20-nvidia.conf.backup.* 2> /dev/null || true
|
||||
return 0
|
||||
# Clean up old backups left by nvidia_troubleshoot.sh
|
||||
rm -f /etc/X11/xorg.conf.d/20-nvidia.conf.backup.* 2>/dev/null || true
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# Fix 2: Power management daemon + performance profile
|
||||
# ===================================================================
|
||||
fix_power_management() {
|
||||
# Install power-profiles-daemon if missing
|
||||
if ! pacman -Qi power-profiles-daemon > /dev/null 2>&1; then
|
||||
log_info "Installing power-profiles-daemon..."
|
||||
pacman -S --needed --noconfirm power-profiles-daemon
|
||||
fi
|
||||
# Install power-profiles-daemon if missing
|
||||
if ! pacman -Qi power-profiles-daemon >/dev/null 2>&1; then
|
||||
log_info "Installing power-profiles-daemon..."
|
||||
pacman -S --needed --noconfirm power-profiles-daemon
|
||||
fi
|
||||
|
||||
# Enable and start the service
|
||||
if ! systemctl is-enabled power-profiles-daemon.service > /dev/null 2>&1; then
|
||||
systemctl enable --now power-profiles-daemon.service
|
||||
elif ! systemctl is-active power-profiles-daemon.service > /dev/null 2>&1; then
|
||||
systemctl start power-profiles-daemon.service
|
||||
fi
|
||||
# Enable and start the service
|
||||
if ! systemctl is-enabled power-profiles-daemon.service >/dev/null 2>&1; then
|
||||
systemctl enable --now power-profiles-daemon.service
|
||||
elif ! systemctl is-active power-profiles-daemon.service >/dev/null 2>&1; then
|
||||
systemctl start power-profiles-daemon.service
|
||||
fi
|
||||
|
||||
# Resolve TLP conflict if both are enabled
|
||||
if systemctl is-enabled tlp.service > /dev/null 2>&1; then
|
||||
log_warn "TLP conflicts with power-profiles-daemon — disabling TLP."
|
||||
systemctl disable --now tlp.service
|
||||
fi
|
||||
# Resolve TLP conflict if both are enabled
|
||||
if systemctl is-enabled tlp.service >/dev/null 2>&1; then
|
||||
log_warn "TLP conflicts with power-profiles-daemon — disabling TLP."
|
||||
systemctl disable --now tlp.service
|
||||
fi
|
||||
|
||||
# Set performance profile (appropriate when plugged in with strong hardware)
|
||||
sleep 1
|
||||
if has_cmd powerprofilesctl; then
|
||||
powerprofilesctl set performance
|
||||
log_info "Power profile set to: $(powerprofilesctl get)"
|
||||
fi
|
||||
# Set performance profile (appropriate when plugged in with strong hardware)
|
||||
sleep 1
|
||||
if has_cmd powerprofilesctl; then
|
||||
powerprofilesctl set performance
|
||||
log_info "Power profile set to: $(powerprofilesctl get)"
|
||||
fi
|
||||
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# Fix 3: Journal vacuum + permanent size cap
|
||||
# ===================================================================
|
||||
fix_journal() {
|
||||
local usage_line
|
||||
usage_line=$(journalctl --disk-usage 2> /dev/null || true)
|
||||
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
|
||||
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 is already under 1GiB."
|
||||
fi
|
||||
if [[ $needs_vacuum == "true" ]]; then
|
||||
journalctl --vacuum-size=300M
|
||||
else
|
||||
log_ok "Journal is already under 1GiB."
|
||||
fi
|
||||
|
||||
# Create permanent size cap via drop-in
|
||||
local dropin_dir="/etc/systemd/journald.conf.d"
|
||||
local dropin_file="$dropin_dir/size-limit.conf"
|
||||
# Create permanent size cap via drop-in
|
||||
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'
|
||||
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
|
||||
systemctl restart systemd-journald
|
||||
fi
|
||||
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# Fix 4: Disable NetworkManager-wait-online
|
||||
# ===================================================================
|
||||
fix_nm_wait_online() {
|
||||
if ! systemctl is-enabled NetworkManager-wait-online.service > /dev/null 2>&1; then
|
||||
log_ok "NetworkManager-wait-online is already disabled — skipping."
|
||||
return 0
|
||||
fi
|
||||
if ! systemctl is-enabled NetworkManager-wait-online.service >/dev/null 2>&1; then
|
||||
log_ok "NetworkManager-wait-online is already disabled — skipping."
|
||||
return 0
|
||||
fi
|
||||
|
||||
systemctl disable NetworkManager-wait-online.service
|
||||
return 0
|
||||
systemctl disable NetworkManager-wait-online.service
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# Fix 5: media-organizer.service
|
||||
# ===================================================================
|
||||
fix_media_organizer() {
|
||||
local service_file="/etc/systemd/system/media-organizer.service"
|
||||
local service_file="/etc/systemd/system/media-organizer.service"
|
||||
|
||||
# Find the organize_downloads.sh script
|
||||
local script_path=""
|
||||
local candidates=(
|
||||
"/home/kuhy/testsAndMisc/linux_configuration/scripts/utils/organize_downloads.sh"
|
||||
"/home/kuhy/linux-configuration/scripts/utils/organize_downloads.sh"
|
||||
)
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if [[ -f $candidate ]]; then
|
||||
script_path="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# Find the organize_downloads.sh script
|
||||
local script_path=""
|
||||
local candidates=(
|
||||
"/home/kuhy/testsAndMisc/linux_configuration/scripts/utils/organize_downloads.sh"
|
||||
"/home/kuhy/linux-configuration/scripts/utils/organize_downloads.sh"
|
||||
)
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if [[ -f $candidate ]]; then
|
||||
script_path="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z $script_path ]]; then
|
||||
log_warn "organize_downloads.sh not found — skipping media-organizer fix."
|
||||
return 0
|
||||
fi
|
||||
if [[ -z $script_path ]]; then
|
||||
log_warn "organize_downloads.sh not found — skipping media-organizer fix."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local target_user="${SUDO_USER:-kuhy}"
|
||||
local target_user="${SUDO_USER:-kuhy}"
|
||||
|
||||
# Check if already correct
|
||||
if [[ -f $service_file ]]; then
|
||||
if grep -q "User=$target_user" "$service_file" \
|
||||
&& grep -q "ExecStart=$script_path" "$service_file"; then
|
||||
log_ok "media-organizer.service is already correctly configured — skipping."
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# Check if already correct
|
||||
if [[ -f $service_file ]]; then
|
||||
if grep -q "User=$target_user" "$service_file" &&
|
||||
grep -q "ExecStart=$script_path" "$service_file"; then
|
||||
log_ok "media-organizer.service is already correctly configured — skipping."
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
systemctl stop media-organizer.service 2> /dev/null || true
|
||||
systemctl stop media-organizer.service 2>/dev/null || true
|
||||
|
||||
cat > "$service_file" << EOF
|
||||
cat >"$service_file" <<EOF
|
||||
[Unit]
|
||||
Description=Media File Organizer
|
||||
After=graphical-session.target
|
||||
@ -271,56 +271,56 @@ RemainAfterExit=no
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed media-organizer.service 2> /dev/null || true
|
||||
systemctl enable media-organizer.service
|
||||
return 0
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed media-organizer.service 2>/dev/null || true
|
||||
systemctl enable media-organizer.service
|
||||
return 0
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# Apply all fixes
|
||||
# ===================================================================
|
||||
main() {
|
||||
apply_fix \
|
||||
"Fix 1/5: Enable NVIDIA hardware acceleration (RenderAccel → true)" \
|
||||
fix_nvidia_render_accel
|
||||
apply_fix \
|
||||
"Fix 1/5: Enable NVIDIA hardware acceleration (RenderAccel → true)" \
|
||||
fix_nvidia_render_accel
|
||||
|
||||
apply_fix \
|
||||
"Fix 2/5: Install/enable power-profiles-daemon + set performance profile" \
|
||||
fix_power_management
|
||||
apply_fix \
|
||||
"Fix 2/5: Install/enable power-profiles-daemon + set performance profile" \
|
||||
fix_power_management
|
||||
|
||||
apply_fix \
|
||||
"Fix 3/5: Vacuum journal logs + set permanent 300M size cap" \
|
||||
fix_journal
|
||||
apply_fix \
|
||||
"Fix 3/5: Vacuum journal logs + set permanent 300M size cap" \
|
||||
fix_journal
|
||||
|
||||
apply_fix \
|
||||
"Fix 4/5: Disable NetworkManager-wait-online.service (~6s boot saving)" \
|
||||
fix_nm_wait_online
|
||||
apply_fix \
|
||||
"Fix 4/5: Disable NetworkManager-wait-online.service (~6s boot saving)" \
|
||||
fix_nm_wait_online
|
||||
|
||||
apply_fix \
|
||||
"Fix 5/5: Fix media-organizer.service (correct path and user)" \
|
||||
fix_media_organizer
|
||||
apply_fix \
|
||||
"Fix 5/5: Fix media-organizer.service (correct path and user)" \
|
||||
fix_media_organizer
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Summary
|
||||
# ---------------------------------------------------------------
|
||||
echo ""
|
||||
echo "=============================="
|
||||
echo " Performance Fix Summary"
|
||||
echo "=============================="
|
||||
# ---------------------------------------------------------------
|
||||
# Summary
|
||||
# ---------------------------------------------------------------
|
||||
echo ""
|
||||
echo "=============================="
|
||||
echo " Performance Fix Summary"
|
||||
echo "=============================="
|
||||
|
||||
if [[ $DRY_RUN == "true" ]]; then
|
||||
log_info "Dry-run mode — no changes were made."
|
||||
else
|
||||
log_ok "Fixes applied: $FIXES_APPLIED"
|
||||
if [[ $FIXES_SKIPPED -gt 0 ]]; then
|
||||
log_warn "Fixes skipped: $FIXES_SKIPPED"
|
||||
fi
|
||||
fi
|
||||
if [[ $DRY_RUN == "true" ]]; then
|
||||
log_info "Dry-run mode — no changes were made."
|
||||
else
|
||||
log_ok "Fixes applied: $FIXES_APPLIED"
|
||||
if [[ $FIXES_SKIPPED -gt 0 ]]; then
|
||||
log_warn "Fixes skipped: $FIXES_SKIPPED"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Reboot or re-login for xorg changes (Fix 1) to take effect."
|
||||
log_info "After reboot, verify with: diagnose_arch_performance.sh"
|
||||
echo ""
|
||||
log_info "Reboot or re-login for xorg changes (Fix 1) to take effect."
|
||||
log_info "After reboot, verify with: diagnose_arch_performance.sh"
|
||||
}
|
||||
|
||||
main
|
||||
|
||||
@ -14,39 +14,39 @@ SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
DEFAULT_ORGANIZE_SCRIPT="/home/kuhy/testsAndMisc/linux_configuration/scripts/utils/organize_downloads.sh"
|
||||
LEGACY_ORGANIZE_SCRIPT="/home/kuhy/linux-configuration/scripts/utils/organize_downloads.sh"
|
||||
if [[ -f $DEFAULT_ORGANIZE_SCRIPT ]]; then
|
||||
ORGANIZE_SCRIPT="$DEFAULT_ORGANIZE_SCRIPT"
|
||||
ORGANIZE_SCRIPT="$DEFAULT_ORGANIZE_SCRIPT"
|
||||
elif [[ -f $LEGACY_ORGANIZE_SCRIPT ]]; then
|
||||
ORGANIZE_SCRIPT="$LEGACY_ORGANIZE_SCRIPT"
|
||||
ORGANIZE_SCRIPT="$LEGACY_ORGANIZE_SCRIPT"
|
||||
else
|
||||
ORGANIZE_SCRIPT="$DEFAULT_ORGANIZE_SCRIPT"
|
||||
ORGANIZE_SCRIPT="$DEFAULT_ORGANIZE_SCRIPT"
|
||||
fi
|
||||
|
||||
TARGET_USER="${SUDO_USER:-kuhy}"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
log "This script needs to be run as root."
|
||||
log "Re-executing with sudo..."
|
||||
exec sudo "$0" "$@"
|
||||
log "This script needs to be run as root."
|
||||
log "Re-executing with sudo..."
|
||||
exec sudo "$0" "$@"
|
||||
fi
|
||||
|
||||
log "Fixing media-organizer.service..."
|
||||
|
||||
# Verify the organize_downloads.sh script exists
|
||||
if [[ ! -f $ORGANIZE_SCRIPT ]]; then
|
||||
log "ERROR: organize_downloads.sh not found at $ORGANIZE_SCRIPT"
|
||||
exit 1
|
||||
log "ERROR: organize_downloads.sh not found at $ORGANIZE_SCRIPT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Stop the service if running (ignore errors)
|
||||
systemctl stop "$SERVICE_NAME.service" 2> /dev/null || true
|
||||
systemctl stop "$SERVICE_NAME.service" 2>/dev/null || true
|
||||
|
||||
# Recreate the service file with correct configuration
|
||||
cat > "$SERVICE_FILE" << EOF
|
||||
cat >"$SERVICE_FILE" <<EOF
|
||||
[Unit]
|
||||
Description=Media File Organizer
|
||||
After=graphical-session.target
|
||||
@ -72,7 +72,7 @@ systemctl daemon-reload
|
||||
log "Reloaded systemd daemon"
|
||||
|
||||
# Reset the failed state
|
||||
systemctl reset-failed "$SERVICE_NAME.service" 2> /dev/null || true
|
||||
systemctl reset-failed "$SERVICE_NAME.service" 2>/dev/null || true
|
||||
log "Reset failed state"
|
||||
|
||||
# Re-enable the service
|
||||
@ -82,9 +82,9 @@ log "Service enabled"
|
||||
# Optionally start the service to verify it works
|
||||
log "Starting service to verify fix..."
|
||||
if systemctl start "$SERVICE_NAME.service"; then
|
||||
log "SUCCESS: media-organizer.service started successfully!"
|
||||
log "SUCCESS: media-organizer.service started successfully!"
|
||||
else
|
||||
log "WARNING: Service still has issues. Check: journalctl -u $SERVICE_NAME"
|
||||
log "WARNING: Service still has issues. Check: journalctl -u $SERVICE_NAME"
|
||||
fi
|
||||
|
||||
# Show current status
|
||||
|
||||
@ -21,7 +21,7 @@ print_setup_header "NVIDIA Comprehensive Troubleshooter & GSP Disabler"
|
||||
|
||||
# Check if nvidia module is loaded
|
||||
if ! lsmod | grep -q nvidia; then
|
||||
echo "Warning: NVIDIA module not currently loaded"
|
||||
echo "Warning: NVIDIA module not currently loaded"
|
||||
fi
|
||||
|
||||
# Create modprobe configuration directory if it doesn't exist
|
||||
@ -34,7 +34,7 @@ echo "======================================"
|
||||
mkdir -p "$MODPROBE_DIR"
|
||||
|
||||
# Create the configuration file
|
||||
cat > "$CONFIG_FILE" << EOF
|
||||
cat >"$CONFIG_FILE" <<EOF
|
||||
# Disable NVIDIA GSP firmware to prevent Vulkan failures and crashes
|
||||
# Created by nvidia_troubleshoot.sh on $(date)
|
||||
options nvidia NVreg_EnableGpuFirmware=0
|
||||
@ -44,35 +44,35 @@ echo "✓ Configuration written to: $CONFIG_FILE"
|
||||
|
||||
# Function to backup file if it exists
|
||||
backup_file() {
|
||||
local file="$1"
|
||||
if [[ -f $file ]]; then
|
||||
cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
echo "✓ Backed up $file"
|
||||
fi
|
||||
local file="$1"
|
||||
if [[ -f $file ]]; then
|
||||
cp "$file" "$file.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
echo "✓ Backed up $file"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to add or update xorg.conf for RenderAccel
|
||||
configure_xorg() {
|
||||
echo ""
|
||||
echo "2. Configuring Xorg Settings..."
|
||||
echo "==============================="
|
||||
echo ""
|
||||
echo "2. Configuring Xorg Settings..."
|
||||
echo "==============================="
|
||||
|
||||
XORG_CONF="/etc/X11/xorg.conf"
|
||||
XORG_CONF_D="/etc/X11/xorg.conf.d"
|
||||
NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
|
||||
XORG_CONF="/etc/X11/xorg.conf"
|
||||
XORG_CONF_D="/etc/X11/xorg.conf.d"
|
||||
NVIDIA_CONF="$XORG_CONF_D/20-nvidia.conf"
|
||||
|
||||
# Create xorg.conf.d directory if it doesn't exist
|
||||
mkdir -p "$XORG_CONF_D"
|
||||
# Create xorg.conf.d directory if it doesn't exist
|
||||
mkdir -p "$XORG_CONF_D"
|
||||
|
||||
# Backup existing xorg.conf if it exists
|
||||
backup_file "$XORG_CONF"
|
||||
backup_file "$NVIDIA_CONF"
|
||||
# Backup existing xorg.conf if it exists
|
||||
backup_file "$XORG_CONF"
|
||||
backup_file "$NVIDIA_CONF"
|
||||
|
||||
# Create NVIDIA-specific configuration
|
||||
# NOTE: RenderAccel must be "true" (or omitted, since it defaults to true).
|
||||
# Setting it to "false" forces software rendering, causing Xorg to consume
|
||||
# 30%+ CPU on the desktop and making the system feel extremely sluggish.
|
||||
cat > "$NVIDIA_CONF" << EOF
|
||||
# Create NVIDIA-specific configuration
|
||||
# NOTE: RenderAccel must be "true" (or omitted, since it defaults to true).
|
||||
# Setting it to "false" forces software rendering, causing Xorg to consume
|
||||
# 30%+ CPU on the desktop and making the system feel extremely sluggish.
|
||||
cat >"$NVIDIA_CONF" <<EOF
|
||||
# NVIDIA configuration - hardware acceleration enabled
|
||||
# Created by nvidia_troubleshoot.sh on $(date)
|
||||
Section "Device"
|
||||
@ -82,106 +82,106 @@ Section "Device"
|
||||
EndSection
|
||||
EOF
|
||||
|
||||
echo "✓ Created $NVIDIA_CONF with RenderAccel enabled"
|
||||
echo "✓ Created $NVIDIA_CONF with RenderAccel enabled"
|
||||
}
|
||||
|
||||
# Function to add GCC mismatch workaround
|
||||
configure_gcc_workaround() {
|
||||
echo ""
|
||||
echo "3. Configuring GCC Mismatch Workaround..."
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "3. Configuring GCC Mismatch Workaround..."
|
||||
echo "=========================================="
|
||||
|
||||
local PROFILE_FILE="/etc/profile"
|
||||
local timestamp
|
||||
timestamp=$(date)
|
||||
backup_file "$PROFILE_FILE"
|
||||
local PROFILE_FILE="/etc/profile"
|
||||
local timestamp
|
||||
timestamp=$(date)
|
||||
backup_file "$PROFILE_FILE"
|
||||
|
||||
# Check if IGNORE_CC_MISMATCH is already set
|
||||
if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
|
||||
{
|
||||
printf '\n'
|
||||
printf '# NVIDIA GCC version mismatch workaround\n'
|
||||
printf '# Added by nvidia_troubleshoot.sh on %s\n' "$timestamp"
|
||||
printf 'export IGNORE_CC_MISMATCH=1\n'
|
||||
} >> "$PROFILE_FILE"
|
||||
echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE"
|
||||
else
|
||||
echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE"
|
||||
fi
|
||||
# Check if IGNORE_CC_MISMATCH is already set
|
||||
if ! grep -q "IGNORE_CC_MISMATCH" "$PROFILE_FILE"; then
|
||||
{
|
||||
printf '\n'
|
||||
printf '# NVIDIA GCC version mismatch workaround\n'
|
||||
printf '# Added by nvidia_troubleshoot.sh on %s\n' "$timestamp"
|
||||
printf 'export IGNORE_CC_MISMATCH=1\n'
|
||||
} >>"$PROFILE_FILE"
|
||||
echo "✓ Added IGNORE_CC_MISMATCH=1 to $PROFILE_FILE"
|
||||
else
|
||||
echo "✓ IGNORE_CC_MISMATCH already configured in $PROFILE_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to install pyroveil for mesh shader issues
|
||||
install_pyroveil() {
|
||||
echo ""
|
||||
echo "4. Pyroveil Setup for Mesh Shader Issues..."
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "4. Pyroveil Setup for Mesh Shader Issues..."
|
||||
echo "==========================================="
|
||||
|
||||
local user_home="/home/$SUDO_USER"
|
||||
local pyroveil_dir="$user_home/pyroveil"
|
||||
local user_home="/home/$SUDO_USER"
|
||||
local pyroveil_dir="$user_home/pyroveil"
|
||||
|
||||
echo "Mesh shaders have poor support on NVIDIA drivers, causing issues in games"
|
||||
echo "like Final Fantasy VII Rebirth. Pyroveil can work around these problems."
|
||||
echo ""
|
||||
echo "Mesh shaders have poor support on NVIDIA drivers, causing issues in games"
|
||||
echo "like Final Fantasy VII Rebirth. Pyroveil can work around these problems."
|
||||
echo ""
|
||||
|
||||
local install_pyroveil=true
|
||||
local install_pyroveil=true
|
||||
|
||||
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||
read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
install_pyroveil=false
|
||||
fi
|
||||
else
|
||||
echo "Auto-installing Pyroveil (use --interactive to prompt)"
|
||||
fi
|
||||
if [[ $INTERACTIVE_MODE == "true" ]]; then
|
||||
read -p "Would you like to install Pyroveil? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
install_pyroveil=false
|
||||
fi
|
||||
else
|
||||
echo "Auto-installing Pyroveil (use --interactive to prompt)"
|
||||
fi
|
||||
|
||||
if [[ $install_pyroveil == "true" ]]; then
|
||||
# Check for required dependencies
|
||||
local missing_deps=()
|
||||
if [[ $install_pyroveil == "true" ]]; then
|
||||
# Check for required dependencies
|
||||
local missing_deps=()
|
||||
|
||||
for dep in git cmake ninja gcc; do
|
||||
if ! command -v "$dep" &> /dev/null; then
|
||||
missing_deps+=("$dep")
|
||||
fi
|
||||
done
|
||||
for dep in git cmake ninja gcc; do
|
||||
if ! command -v "$dep" &>/dev/null; then
|
||||
missing_deps+=("$dep")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
||||
echo "Missing dependencies: ${missing_deps[*]}"
|
||||
echo "Please install them first. On Arch Linux:"
|
||||
echo "pacman -S base-devel git cmake ninja"
|
||||
return 1
|
||||
fi
|
||||
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
||||
echo "Missing dependencies: ${missing_deps[*]}"
|
||||
echo "Please install them first. On Arch Linux:"
|
||||
echo "pacman -S base-devel git cmake ninja"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Clone and build pyroveil as the original user
|
||||
echo "Installing Pyroveil to $pyroveil_dir..."
|
||||
# Clone and build pyroveil as the original user
|
||||
echo "Installing Pyroveil to $pyroveil_dir..."
|
||||
|
||||
if [[ -d $pyroveil_dir ]]; then
|
||||
echo "Pyroveil directory already exists. Updating..."
|
||||
sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
|
||||
else
|
||||
sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir"
|
||||
fi
|
||||
if [[ -d $pyroveil_dir ]]; then
|
||||
echo "Pyroveil directory already exists. Updating..."
|
||||
sudo -u "$SUDO_USER" bash -c "cd '$pyroveil_dir' && git pull"
|
||||
else
|
||||
sudo -u "$SUDO_USER" git clone https://github.com/HansKristian-Work/pyroveil.git "$pyroveil_dir"
|
||||
fi
|
||||
|
||||
sudo -u "$SUDO_USER" bash -c "
|
||||
sudo -u "$SUDO_USER" bash -c "
|
||||
cd '$pyroveil_dir'
|
||||
git submodule update --init
|
||||
cmake . -Bbuild -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$user_home/.local
|
||||
ninja -C build install
|
||||
"
|
||||
|
||||
echo "✓ Pyroveil installed successfully"
|
||||
echo ""
|
||||
echo "To use Pyroveil with games that have mesh shader issues:"
|
||||
echo "1. For Final Fantasy VII Rebirth:"
|
||||
echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
|
||||
echo ""
|
||||
echo "2. For Steam games, add to launch options:"
|
||||
echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
|
||||
echo ""
|
||||
echo "Available configs in: $pyroveil_dir/hacks/"
|
||||
echo "✓ Pyroveil installed successfully"
|
||||
echo ""
|
||||
echo "To use Pyroveil with games that have mesh shader issues:"
|
||||
echo "1. For Final Fantasy VII Rebirth:"
|
||||
echo " PYROVEIL=1 PYROVEIL_CONFIG=$pyroveil_dir/hacks/ffvii-rebirth-nvidia/pyroveil.json %command%"
|
||||
echo ""
|
||||
echo "2. For Steam games, add to launch options:"
|
||||
echo " PYROVEIL=1 PYROVEIL_CONFIG=/path/to/config/pyroveil.json %command%"
|
||||
echo ""
|
||||
echo "Available configs in: $pyroveil_dir/hacks/"
|
||||
|
||||
# Create a helper script
|
||||
cat > "$user_home/run-with-pyroveil.sh" << EOF
|
||||
# Create a helper script
|
||||
cat >"$user_home/run-with-pyroveil.sh" <<EOF
|
||||
#!/bin/bash
|
||||
# Helper script to run games with Pyroveil
|
||||
# Usage: ./run-with-pyroveil.sh <config-name> <command>
|
||||
@ -207,88 +207,88 @@ echo "Config file: \$PYROVEIL_CONFIG"
|
||||
exec "\$@"
|
||||
EOF
|
||||
|
||||
chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh"
|
||||
chmod +x "$user_home/run-with-pyroveil.sh"
|
||||
echo "✓ Created helper script: $user_home/run-with-pyroveil.sh"
|
||||
chown "$SUDO_USER:$SUDO_USER" "$user_home/run-with-pyroveil.sh"
|
||||
chmod +x "$user_home/run-with-pyroveil.sh"
|
||||
echo "✓ Created helper script: $user_home/run-with-pyroveil.sh"
|
||||
|
||||
else
|
||||
echo "Skipping Pyroveil installation"
|
||||
echo "Note: You can manually install it later for mesh shader issues"
|
||||
fi
|
||||
else
|
||||
echo "Skipping Pyroveil installation"
|
||||
echo "Note: You can manually install it later for mesh shader issues"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check for kernel parameter modifications
|
||||
suggest_kernel_params() {
|
||||
echo ""
|
||||
echo "5. Kernel Parameter Recommendations..."
|
||||
echo "====================================="
|
||||
echo ""
|
||||
echo "5. Kernel Parameter Recommendations..."
|
||||
echo "====================================="
|
||||
|
||||
echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
|
||||
echo ""
|
||||
echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
|
||||
echo " (especially with nvidia-96xx drivers):"
|
||||
echo " → Add 'nopat' to kernel parameters"
|
||||
echo ""
|
||||
echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
|
||||
echo " → Consider disabling micro-op cache in BIOS settings"
|
||||
echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
|
||||
echo " → Helps with severe graphical glitches in Xwayland applications"
|
||||
echo " → Note: Disabling micro-op cache reduces CPU performance"
|
||||
echo ""
|
||||
echo "To add kernel parameters:"
|
||||
echo "1. Edit /etc/default/grub"
|
||||
echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
|
||||
echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
|
||||
echo "4. Reboot"
|
||||
echo ""
|
||||
echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
|
||||
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
|
||||
echo "NVIDIA Driver Issues and Recommended Kernel Parameters:"
|
||||
echo ""
|
||||
echo "A) For 'conflicting memory type' or 'failed to allocate primary buffer' errors"
|
||||
echo " (especially with nvidia-96xx drivers):"
|
||||
echo " → Add 'nopat' to kernel parameters"
|
||||
echo ""
|
||||
echo "B) For OpenGL visual glitches, hangs, and errors with modern CPUs:"
|
||||
echo " → Consider disabling micro-op cache in BIOS settings"
|
||||
echo " → This affects Intel Sandy Bridge (2011+) and AMD Zen (2017+) CPUs"
|
||||
echo " → Helps with severe graphical glitches in Xwayland applications"
|
||||
echo " → Note: Disabling micro-op cache reduces CPU performance"
|
||||
echo ""
|
||||
echo "To add kernel parameters:"
|
||||
echo "1. Edit /etc/default/grub"
|
||||
echo "2. Add parameters to GRUB_CMDLINE_LINUX_DEFAULT"
|
||||
echo "3. Run: grub-mkconfig -o /boot/grub/grub.cfg"
|
||||
echo "4. Reboot"
|
||||
echo ""
|
||||
echo "Example GRUB_CMDLINE_LINUX_DEFAULT line:"
|
||||
echo 'GRUB_CMDLINE_LINUX_DEFAULT="quiet nopat"'
|
||||
|
||||
# Check current CPU for micro-op cache relevance
|
||||
echo ""
|
||||
echo "CPU Information (for micro-op cache consideration):"
|
||||
if command -v lscpu &> /dev/null; then
|
||||
local cpu_info
|
||||
cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs)
|
||||
echo "Current CPU: $cpu_info"
|
||||
# Check current CPU for micro-op cache relevance
|
||||
echo ""
|
||||
echo "CPU Information (for micro-op cache consideration):"
|
||||
if command -v lscpu &>/dev/null; then
|
||||
local cpu_info
|
||||
cpu_info=$(lscpu | grep "Model name" | cut -d: -f2 | xargs)
|
||||
echo "Current CPU: $cpu_info"
|
||||
|
||||
if echo "$cpu_info" | grep -qi "intel"; then
|
||||
echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache"
|
||||
elif echo "$cpu_info" | grep -qi "amd"; then
|
||||
echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
|
||||
fi
|
||||
fi
|
||||
if echo "$cpu_info" | grep -qi "intel"; then
|
||||
echo "→ Intel CPU detected. Sandy Bridge (2011) and later have micro-op cache"
|
||||
elif echo "$cpu_info" | grep -qi "amd"; then
|
||||
echo "→ AMD CPU detected. Zen (2017) and later have micro-op cache"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to suggest desktop environment settings
|
||||
suggest_desktop_settings() {
|
||||
echo ""
|
||||
echo "6. Desktop Environment Recommendations..."
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "6. Desktop Environment Recommendations..."
|
||||
echo "========================================"
|
||||
|
||||
echo "For fullscreen application freezing/crashing issues:"
|
||||
echo ""
|
||||
echo "Enable Display Compositing and Direct fullscreen rendering:"
|
||||
echo ""
|
||||
echo "• KDE Plasma:"
|
||||
echo " System Settings → Display and Monitor → Compositor"
|
||||
echo " → Enable compositor + Enable direct rendering for fullscreen windows"
|
||||
echo ""
|
||||
echo "• GNOME:"
|
||||
echo " Use Extensions or dconf-editor to enable compositing features"
|
||||
echo ""
|
||||
echo "• XFCE:"
|
||||
echo " Settings → Window Manager Tweaks → Compositor"
|
||||
echo " → Enable display compositing"
|
||||
echo ""
|
||||
echo "• Cinnamon:"
|
||||
echo " System Settings → Effects → Enable desktop effects"
|
||||
echo "For fullscreen application freezing/crashing issues:"
|
||||
echo ""
|
||||
echo "Enable Display Compositing and Direct fullscreen rendering:"
|
||||
echo ""
|
||||
echo "• KDE Plasma:"
|
||||
echo " System Settings → Display and Monitor → Compositor"
|
||||
echo " → Enable compositor + Enable direct rendering for fullscreen windows"
|
||||
echo ""
|
||||
echo "• GNOME:"
|
||||
echo " Use Extensions or dconf-editor to enable compositing features"
|
||||
echo ""
|
||||
echo "• XFCE:"
|
||||
echo " Settings → Window Manager Tweaks → Compositor"
|
||||
echo " → Enable display compositing"
|
||||
echo ""
|
||||
echo "• Cinnamon:"
|
||||
echo " System Settings → Effects → Enable desktop effects"
|
||||
|
||||
# Detect current desktop environment
|
||||
if [[ -n $XDG_CURRENT_DESKTOP ]]; then
|
||||
echo ""
|
||||
echo "Detected desktop environment: $XDG_CURRENT_DESKTOP"
|
||||
fi
|
||||
# Detect current desktop environment
|
||||
if [[ -n $XDG_CURRENT_DESKTOP ]]; then
|
||||
echo ""
|
||||
echo "Detected desktop environment: $XDG_CURRENT_DESKTOP"
|
||||
fi
|
||||
}
|
||||
|
||||
# Apply all configurations
|
||||
@ -300,14 +300,14 @@ install_pyroveil
|
||||
echo ""
|
||||
echo "7. Regenerating Initramfs..."
|
||||
echo "============================"
|
||||
if command -v mkinitcpio &> /dev/null; then
|
||||
mkinitcpio -P
|
||||
echo "✓ Initramfs regenerated with mkinitcpio"
|
||||
elif command -v dracut &> /dev/null; then
|
||||
dracut --force
|
||||
echo "✓ Initramfs regenerated with dracut"
|
||||
if command -v mkinitcpio &>/dev/null; then
|
||||
mkinitcpio -P
|
||||
echo "✓ Initramfs regenerated with mkinitcpio"
|
||||
elif command -v dracut &>/dev/null; then
|
||||
dracut --force
|
||||
echo "✓ Initramfs regenerated with dracut"
|
||||
else
|
||||
echo "Warning: Could not find mkinitcpio or dracut. You may need to manually regenerate initramfs."
|
||||
echo "Warning: Could not find mkinitcpio or dracut. You may need to manually regenerate initramfs."
|
||||
fi
|
||||
|
||||
# Display all recommendations
|
||||
@ -323,7 +323,7 @@ echo "✓ GSP firmware disabled"
|
||||
echo "✓ RenderAccel disabled in Xorg configuration"
|
||||
echo "✓ GCC version mismatch workaround added"
|
||||
if [[ -d "/home/$SUDO_USER/pyroveil" ]]; then
|
||||
echo "✓ Pyroveil installed for mesh shader issues"
|
||||
echo "✓ Pyroveil installed for mesh shader issues"
|
||||
fi
|
||||
echo "✓ Initramfs regenerated"
|
||||
echo ""
|
||||
|
||||
@ -19,8 +19,8 @@ GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||
|
||||
WITH_SERVER=false
|
||||
@ -30,154 +30,160 @@ DUCKDNS_DOMAIN=""
|
||||
DUCKDNS_TOKEN=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--with-server) WITH_SERVER=true ;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 [--with-server]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --with-server Also set up Joplin Server via Docker"
|
||||
echo " --help, -h Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
error "Unknown argument: $arg"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
case "$arg" in
|
||||
--with-server) WITH_SERVER=true ;;
|
||||
--help | -h)
|
||||
echo "Usage: $0 [--with-server]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --with-server Also set up Joplin Server via Docker"
|
||||
echo " --help, -h Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
error "Unknown argument: $arg"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Check prerequisites ─────────────────────────────────────────────
|
||||
command -v pacman >/dev/null 2>&1 || { error "pacman not found. This script is for Arch Linux."; exit 1; }
|
||||
command -v pacman >/dev/null 2>&1 || {
|
||||
error "pacman not found. This script is for Arch Linux."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ── Install Joplin Desktop ──────────────────────────────────────────
|
||||
install_joplin_desktop() {
|
||||
if [[ -f "$HOME/.joplin/Joplin.AppImage" ]]; then
|
||||
info "Joplin desktop is already installed at $HOME/.joplin/Joplin.AppImage"
|
||||
return
|
||||
fi
|
||||
if [[ -f "$HOME/.joplin/Joplin.AppImage" ]]; then
|
||||
info "Joplin desktop is already installed at $HOME/.joplin/Joplin.AppImage"
|
||||
return
|
||||
fi
|
||||
|
||||
info "Installing Joplin desktop app via official installer (AppImage)..."
|
||||
info "Installing Joplin desktop app via official installer (AppImage)..."
|
||||
|
||||
# Official Joplin install script downloads the latest AppImage
|
||||
wget -O - https://raw.githubusercontent.com/laurent22/joplin/dev/Joplin_install_and_update.sh | bash
|
||||
# Official Joplin install script downloads the latest AppImage
|
||||
wget -O - https://raw.githubusercontent.com/laurent22/joplin/dev/Joplin_install_and_update.sh | bash
|
||||
|
||||
info "Joplin desktop installed at ~/.joplin/Joplin.AppImage"
|
||||
info "Launch with: ~/.joplin/Joplin.AppImage (or 'joplin-desktop' from menu)"
|
||||
info "Joplin desktop installed at ~/.joplin/Joplin.AppImage"
|
||||
info "Launch with: ~/.joplin/Joplin.AppImage (or 'joplin-desktop' from menu)"
|
||||
}
|
||||
|
||||
# ── Set up DuckDNS for stable URL ───────────────────────────────────
|
||||
setup_duckdns() {
|
||||
info "Setting up DuckDNS for stable server URL..."
|
||||
info "Setting up DuckDNS for stable server URL..."
|
||||
|
||||
if [[ -z "$DUCKDNS_DOMAIN" ]]; then
|
||||
echo ""
|
||||
info "Your public IP may change. DuckDNS provides a free stable hostname."
|
||||
info "1. Go to https://www.duckdns.org/ and sign in (Google/GitHub/etc.)"
|
||||
info "2. Create a subdomain (e.g. 'myjoplin' for myjoplin.duckdns.org)"
|
||||
info "3. Copy your token from the DuckDNS dashboard"
|
||||
echo ""
|
||||
read -r -p "Enter your DuckDNS subdomain (without .duckdns.org): " DUCKDNS_DOMAIN
|
||||
read -r -p "Enter your DuckDNS token: " DUCKDNS_TOKEN
|
||||
fi
|
||||
if [[ -z "$DUCKDNS_DOMAIN" ]]; then
|
||||
echo ""
|
||||
info "Your public IP may change. DuckDNS provides a free stable hostname."
|
||||
info "1. Go to https://www.duckdns.org/ and sign in (Google/GitHub/etc.)"
|
||||
info "2. Create a subdomain (e.g. 'myjoplin' for myjoplin.duckdns.org)"
|
||||
info "3. Copy your token from the DuckDNS dashboard"
|
||||
echo ""
|
||||
read -r -p "Enter your DuckDNS subdomain (without .duckdns.org): " DUCKDNS_DOMAIN
|
||||
read -r -p "Enter your DuckDNS token: " DUCKDNS_TOKEN
|
||||
fi
|
||||
|
||||
if [[ -z "$DUCKDNS_DOMAIN" ]] || [[ -z "$DUCKDNS_TOKEN" ]]; then
|
||||
warn "DuckDNS not configured. Falling back to raw public IP (may change!)."
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$DUCKDNS_DOMAIN" ]] || [[ -z "$DUCKDNS_TOKEN" ]]; then
|
||||
warn "DuckDNS not configured. Falling back to raw public IP (may change!)."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local full_domain="${DUCKDNS_DOMAIN}.duckdns.org"
|
||||
local full_domain="${DUCKDNS_DOMAIN}.duckdns.org"
|
||||
|
||||
# Update DuckDNS now
|
||||
info "Updating DuckDNS record for ${full_domain}..."
|
||||
local result
|
||||
result=$(curl -s "https://www.duckdns.org/update?domains=${DUCKDNS_DOMAIN}&token=${DUCKDNS_TOKEN}&ip=")
|
||||
if [[ "$result" == "OK" ]]; then
|
||||
info "DuckDNS updated successfully: ${full_domain}"
|
||||
else
|
||||
warn "DuckDNS update returned: $result"
|
||||
fi
|
||||
# Update DuckDNS now
|
||||
info "Updating DuckDNS record for ${full_domain}..."
|
||||
local result
|
||||
result=$(curl -s "https://www.duckdns.org/update?domains=${DUCKDNS_DOMAIN}&token=${DUCKDNS_TOKEN}&ip=")
|
||||
if [[ "$result" == "OK" ]]; then
|
||||
info "DuckDNS updated successfully: ${full_domain}"
|
||||
else
|
||||
warn "DuckDNS update returned: $result"
|
||||
fi
|
||||
|
||||
# Set up cron job to keep IP updated every 5 minutes
|
||||
local duckdns_script="$JOPLIN_DATA_DIR/duckdns-update.sh"
|
||||
cat > "$duckdns_script" <<DUCKEOF
|
||||
# Set up cron job to keep IP updated every 5 minutes
|
||||
local duckdns_script="$JOPLIN_DATA_DIR/duckdns-update.sh"
|
||||
cat >"$duckdns_script" <<DUCKEOF
|
||||
#!/usr/bin/env bash
|
||||
result=\$(curl -s "https://www.duckdns.org/update?domains=${DUCKDNS_DOMAIN}&token=${DUCKDNS_TOKEN}&ip=")
|
||||
echo "\$(date): \$result" >> "$JOPLIN_DATA_DIR/duckdns.log"
|
||||
DUCKEOF
|
||||
chmod +x "$duckdns_script"
|
||||
chmod +x "$duckdns_script"
|
||||
|
||||
# Add cron job (remove old one if exists, add new)
|
||||
(crontab -l 2>/dev/null | grep -v "duckdns-update.sh"; echo "*/5 * * * * $duckdns_script") | crontab -
|
||||
info "DuckDNS cron job installed (updates every 5 minutes)"
|
||||
# Add cron job (remove old one if exists, add new)
|
||||
(
|
||||
crontab -l 2>/dev/null | grep -v "duckdns-update.sh"
|
||||
echo "*/5 * * * * $duckdns_script"
|
||||
) | crontab -
|
||||
info "DuckDNS cron job installed (updates every 5 minutes)"
|
||||
|
||||
# Save config for future runs
|
||||
local config_file="$JOPLIN_DATA_DIR/.duckdns.conf"
|
||||
cat > "$config_file" <<CONFEOF
|
||||
# Save config for future runs
|
||||
local config_file="$JOPLIN_DATA_DIR/.duckdns.conf"
|
||||
cat >"$config_file" <<CONFEOF
|
||||
DUCKDNS_DOMAIN="${DUCKDNS_DOMAIN}"
|
||||
DUCKDNS_TOKEN="${DUCKDNS_TOKEN}"
|
||||
CONFEOF
|
||||
chmod 600 "$config_file"
|
||||
chmod 600 "$config_file"
|
||||
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Set up Joplin Server (Docker) ───────────────────────────────────
|
||||
setup_joplin_server() {
|
||||
info "Setting up Joplin Server via Docker..."
|
||||
info "Setting up Joplin Server via Docker..."
|
||||
|
||||
# Ensure Docker is installed and running
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
info "Installing Docker..."
|
||||
sudo pacman -S --needed --noconfirm docker docker-compose
|
||||
fi
|
||||
# Ensure Docker is installed and running
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
info "Installing Docker..."
|
||||
sudo pacman -S --needed --noconfirm docker docker-compose
|
||||
fi
|
||||
|
||||
if ! systemctl is-active --quiet docker; then
|
||||
info "Starting Docker service..."
|
||||
sudo systemctl enable --now docker
|
||||
fi
|
||||
if ! systemctl is-active --quiet docker; then
|
||||
info "Starting Docker service..."
|
||||
sudo systemctl enable --now docker
|
||||
fi
|
||||
|
||||
# Add user to docker group if not already a member
|
||||
if ! groups | grep -q '\bdocker\b'; then
|
||||
warn "Adding $USER to docker group (re-login required for group to take effect)."
|
||||
sudo usermod -aG docker "$USER"
|
||||
fi
|
||||
# Add user to docker group if not already a member
|
||||
if ! groups | grep -q '\bdocker\b'; then
|
||||
warn "Adding $USER to docker group (re-login required for group to take effect)."
|
||||
sudo usermod -aG docker "$USER"
|
||||
fi
|
||||
|
||||
# Create data directory
|
||||
mkdir -p "$JOPLIN_DATA_DIR"
|
||||
# Create data directory
|
||||
mkdir -p "$JOPLIN_DATA_DIR"
|
||||
|
||||
local compose_file="$JOPLIN_DATA_DIR/docker-compose.yml"
|
||||
local compose_file="$JOPLIN_DATA_DIR/docker-compose.yml"
|
||||
|
||||
# Load saved DuckDNS config if it exists
|
||||
if [[ -f "$JOPLIN_DATA_DIR/.duckdns.conf" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "$JOPLIN_DATA_DIR/.duckdns.conf"
|
||||
fi
|
||||
# Load saved DuckDNS config if it exists
|
||||
if [[ -f "$JOPLIN_DATA_DIR/.duckdns.conf" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "$JOPLIN_DATA_DIR/.duckdns.conf"
|
||||
fi
|
||||
|
||||
# Set up DuckDNS for a stable hostname
|
||||
local server_url
|
||||
if setup_duckdns; then
|
||||
server_url="http://${DUCKDNS_DOMAIN}.duckdns.org:${JOPLIN_SERVER_PORT}"
|
||||
info "Using stable DuckDNS URL: $server_url"
|
||||
else
|
||||
# Fallback to public IP
|
||||
local host_ip
|
||||
host_ip="$(curl -s --max-time 5 ifconfig.me 2>/dev/null)"
|
||||
if [[ -z "$host_ip" ]]; then
|
||||
host_ip="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')"
|
||||
fi
|
||||
if [[ -z "$host_ip" ]]; then
|
||||
host_ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||||
fi
|
||||
if [[ -z "$host_ip" ]]; then
|
||||
warn "Could not detect external IP. Falling back to 0.0.0.0"
|
||||
host_ip="0.0.0.0"
|
||||
fi
|
||||
server_url="http://${host_ip}:${JOPLIN_SERVER_PORT}"
|
||||
warn "Using raw IP URL (may change!): $server_url"
|
||||
fi
|
||||
# Set up DuckDNS for a stable hostname
|
||||
local server_url
|
||||
if setup_duckdns; then
|
||||
server_url="http://${DUCKDNS_DOMAIN}.duckdns.org:${JOPLIN_SERVER_PORT}"
|
||||
info "Using stable DuckDNS URL: $server_url"
|
||||
else
|
||||
# Fallback to public IP
|
||||
local host_ip
|
||||
host_ip="$(curl -s --max-time 5 ifconfig.me 2>/dev/null)"
|
||||
if [[ -z "$host_ip" ]]; then
|
||||
host_ip="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')"
|
||||
fi
|
||||
if [[ -z "$host_ip" ]]; then
|
||||
host_ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
|
||||
fi
|
||||
if [[ -z "$host_ip" ]]; then
|
||||
warn "Could not detect external IP. Falling back to 0.0.0.0"
|
||||
host_ip="0.0.0.0"
|
||||
fi
|
||||
server_url="http://${host_ip}:${JOPLIN_SERVER_PORT}"
|
||||
warn "Using raw IP URL (may change!): $server_url"
|
||||
fi
|
||||
|
||||
cat > "$compose_file" <<EOF
|
||||
cat >"$compose_file" <<EOF
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
@ -221,73 +227,73 @@ networks:
|
||||
joplin-net:
|
||||
EOF
|
||||
|
||||
info "Starting Joplin Server..."
|
||||
docker compose -f "$compose_file" up -d
|
||||
info "Starting Joplin Server..."
|
||||
docker compose -f "$compose_file" up -d
|
||||
|
||||
echo ""
|
||||
info "Joplin Server is running at: ${server_url}"
|
||||
echo ""
|
||||
echo " Default admin credentials:"
|
||||
echo " Email: admin@localhost"
|
||||
echo " Password: admin"
|
||||
echo ""
|
||||
warn "IMPORTANT: Change the default admin password and the database"
|
||||
warn "password (POSTGRES_PASSWORD in $compose_file) immediately!"
|
||||
echo ""
|
||||
info "Firewall: opening port ${JOPLIN_SERVER_PORT}/tcp for external access..."
|
||||
if command -v ufw >/dev/null 2>&1; then
|
||||
sudo ufw allow "${JOPLIN_SERVER_PORT}/tcp" || warn "Could not configure ufw"
|
||||
elif command -v firewall-cmd >/dev/null 2>&1; then
|
||||
if sudo firewall-cmd --permanent --add-port="${JOPLIN_SERVER_PORT}/tcp" \
|
||||
&& sudo firewall-cmd --reload; then
|
||||
:
|
||||
else
|
||||
warn "Could not configure firewalld"
|
||||
fi
|
||||
elif command -v iptables >/dev/null 2>&1; then
|
||||
sudo iptables -A INPUT -p tcp --dport "${JOPLIN_SERVER_PORT}" -j ACCEPT || warn "Could not configure iptables"
|
||||
else
|
||||
warn "No firewall tool found. Ensure port ${JOPLIN_SERVER_PORT}/tcp is open manually."
|
||||
fi
|
||||
echo ""
|
||||
echo " To connect Joplin desktop/Android to this server:"
|
||||
echo " 1. Open Joplin → Tools → Options → Synchronisation"
|
||||
echo " 2. Set target to 'Joplin Server'"
|
||||
echo " 3. Enter URL: ${server_url}"
|
||||
echo " 4. Enter your Joplin Server email and password"
|
||||
echo ""
|
||||
echo " Server management:"
|
||||
echo " Start: docker compose -f $compose_file up -d"
|
||||
echo " Stop: docker compose -f $compose_file down"
|
||||
echo " Logs: docker compose -f $compose_file logs -f"
|
||||
echo " Update: docker compose -f $compose_file pull && docker compose -f $compose_file up -d"
|
||||
echo ""
|
||||
info "Joplin Server is running at: ${server_url}"
|
||||
echo ""
|
||||
echo " Default admin credentials:"
|
||||
echo " Email: admin@localhost"
|
||||
echo " Password: admin"
|
||||
echo ""
|
||||
warn "IMPORTANT: Change the default admin password and the database"
|
||||
warn "password (POSTGRES_PASSWORD in $compose_file) immediately!"
|
||||
echo ""
|
||||
info "Firewall: opening port ${JOPLIN_SERVER_PORT}/tcp for external access..."
|
||||
if command -v ufw >/dev/null 2>&1; then
|
||||
sudo ufw allow "${JOPLIN_SERVER_PORT}/tcp" || warn "Could not configure ufw"
|
||||
elif command -v firewall-cmd >/dev/null 2>&1; then
|
||||
if sudo firewall-cmd --permanent --add-port="${JOPLIN_SERVER_PORT}/tcp" &&
|
||||
sudo firewall-cmd --reload; then
|
||||
:
|
||||
else
|
||||
warn "Could not configure firewalld"
|
||||
fi
|
||||
elif command -v iptables >/dev/null 2>&1; then
|
||||
sudo iptables -A INPUT -p tcp --dport "${JOPLIN_SERVER_PORT}" -j ACCEPT || warn "Could not configure iptables"
|
||||
else
|
||||
warn "No firewall tool found. Ensure port ${JOPLIN_SERVER_PORT}/tcp is open manually."
|
||||
fi
|
||||
echo ""
|
||||
echo " To connect Joplin desktop/Android to this server:"
|
||||
echo " 1. Open Joplin → Tools → Options → Synchronisation"
|
||||
echo " 2. Set target to 'Joplin Server'"
|
||||
echo " 3. Enter URL: ${server_url}"
|
||||
echo " 4. Enter your Joplin Server email and password"
|
||||
echo ""
|
||||
echo " Server management:"
|
||||
echo " Start: docker compose -f $compose_file up -d"
|
||||
echo " Stop: docker compose -f $compose_file down"
|
||||
echo " Logs: docker compose -f $compose_file logs -f"
|
||||
echo " Update: docker compose -f $compose_file pull && docker compose -f $compose_file up -d"
|
||||
}
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────────────
|
||||
main() {
|
||||
echo "╔══════════════════════════════════════════════╗"
|
||||
echo "║ Joplin Installation Script ║"
|
||||
echo "║ Free & Open Source Note-Taking App ║"
|
||||
echo "║ https://joplinapp.org ║"
|
||||
echo "╚══════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════╗"
|
||||
echo "║ Joplin Installation Script ║"
|
||||
echo "║ Free & Open Source Note-Taking App ║"
|
||||
echo "║ https://joplinapp.org ║"
|
||||
echo "╚══════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
install_joplin_desktop
|
||||
install_joplin_desktop
|
||||
|
||||
if [[ "$WITH_SERVER" == true ]]; then
|
||||
setup_joplin_server
|
||||
else
|
||||
echo ""
|
||||
info "Tip: Run with --with-server to also set up Joplin Server"
|
||||
info "for self-hosted sync across devices (desktop + Android)."
|
||||
fi
|
||||
if [[ "$WITH_SERVER" == true ]]; then
|
||||
setup_joplin_server
|
||||
else
|
||||
echo ""
|
||||
info "Tip: Run with --with-server to also set up Joplin Server"
|
||||
info "for self-hosted sync across devices (desktop + Android)."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
info "Android app available at:"
|
||||
info " Google Play: https://play.google.com/store/apps/details?id=net.cozic.joplin"
|
||||
info " F-Droid: https://f-droid.org/packages/net.cozic.joplin/"
|
||||
echo ""
|
||||
info "Done!"
|
||||
echo ""
|
||||
info "Android app available at:"
|
||||
info " Google Play: https://play.google.com/store/apps/details?id=net.cozic.joplin"
|
||||
info " F-Droid: https://f-droid.org/packages/net.cozic.joplin/"
|
||||
echo ""
|
||||
info "Done!"
|
||||
}
|
||||
|
||||
main
|
||||
|
||||
@ -20,11 +20,11 @@ REMOVE_FILE="$MODULE_DIR/remove"
|
||||
mkdir -p "$GUARDIAN_DIR"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >>"$LOG_FILE"
|
||||
}
|
||||
|
||||
# Initialize control file if not exists
|
||||
[ ! -f "$CONTROL_FILE" ] && echo "ENABLED" > "$CONTROL_FILE"
|
||||
[ ! -f "$CONTROL_FILE" ] && echo "ENABLED" >"$CONTROL_FILE"
|
||||
|
||||
log "=== Android Guardian starting ==="
|
||||
|
||||
@ -36,73 +36,73 @@ log "Wireless ADB enabled on port 5555"
|
||||
|
||||
# Function to check if guardian is enabled (via ADB control, not Magisk UI)
|
||||
is_enabled() {
|
||||
[ "$(cat "$CONTROL_FILE" 2> /dev/null)" = "ENABLED" ]
|
||||
[ "$(cat "$CONTROL_FILE" 2>/dev/null)" = "ENABLED" ]
|
||||
}
|
||||
|
||||
# Function to protect module from being disabled via Magisk UI
|
||||
protect_module() {
|
||||
# Remove disable file if someone tried to disable via Magisk
|
||||
if [ -f "$DISABLE_FILE" ]; then
|
||||
log "Module disable attempt detected via Magisk UI! Re-enabling..."
|
||||
rm -f "$DISABLE_FILE"
|
||||
log "Module re-enabled"
|
||||
fi
|
||||
# Remove disable file if someone tried to disable via Magisk
|
||||
if [ -f "$DISABLE_FILE" ]; then
|
||||
log "Module disable attempt detected via Magisk UI! Re-enabling..."
|
||||
rm -f "$DISABLE_FILE"
|
||||
log "Module re-enabled"
|
||||
fi
|
||||
|
||||
# Remove remove file if someone tried to uninstall via Magisk
|
||||
if [ -f "$REMOVE_FILE" ]; then
|
||||
log "Module removal attempt detected via Magisk UI! Blocking..."
|
||||
rm -f "$REMOVE_FILE"
|
||||
log "Module removal blocked"
|
||||
fi
|
||||
# Remove remove file if someone tried to uninstall via Magisk
|
||||
if [ -f "$REMOVE_FILE" ]; then
|
||||
log "Module removal attempt detected via Magisk UI! Blocking..."
|
||||
rm -f "$REMOVE_FILE"
|
||||
log "Module removal blocked"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to restore hosts file if tampered
|
||||
protect_hosts() {
|
||||
if [ -f "$HOSTS_BACKUP" ]; then
|
||||
current_hash=$(md5sum /system/etc/hosts 2> /dev/null | cut -d' ' -f1)
|
||||
backup_hash=$(md5sum "$HOSTS_BACKUP" 2> /dev/null | cut -d' ' -f1)
|
||||
if [ -f "$HOSTS_BACKUP" ]; then
|
||||
current_hash=$(md5sum /system/etc/hosts 2>/dev/null | cut -d' ' -f1)
|
||||
backup_hash=$(md5sum "$HOSTS_BACKUP" 2>/dev/null | cut -d' ' -f1)
|
||||
|
||||
if [ "$current_hash" != "$backup_hash" ]; then
|
||||
log "Hosts file tampering detected! Restoring..."
|
||||
cp "$HOSTS_BACKUP" "$MODDIR/system/etc/hosts"
|
||||
log "Hosts file restored"
|
||||
fi
|
||||
fi
|
||||
if [ "$current_hash" != "$backup_hash" ]; then
|
||||
log "Hosts file tampering detected! Restoring..."
|
||||
cp "$HOSTS_BACKUP" "$MODDIR/system/etc/hosts"
|
||||
log "Hosts file restored"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to uninstall blocked apps
|
||||
check_blocked_apps() {
|
||||
if [ ! -f "$BLOCKED_APPS_FILE" ]; then
|
||||
return
|
||||
fi
|
||||
if [ ! -f "$BLOCKED_APPS_FILE" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
while IFS= read -r package || [ -n "$package" ]; do
|
||||
# Skip comments and empty lines
|
||||
case "$package" in
|
||||
\#* | "") continue ;;
|
||||
esac
|
||||
while IFS= read -r package || [ -n "$package" ]; do
|
||||
# Skip comments and empty lines
|
||||
case "$package" in
|
||||
\#* | "") continue ;;
|
||||
esac
|
||||
|
||||
# Check if package is installed
|
||||
if pm list packages 2> /dev/null | grep -q "package:$package"; then
|
||||
log "Blocked app detected: $package - Uninstalling..."
|
||||
pm uninstall "$package" 2> /dev/null && log "Uninstalled: $package" || log "Failed to uninstall: $package"
|
||||
fi
|
||||
done < "$BLOCKED_APPS_FILE"
|
||||
# Check if package is installed
|
||||
if pm list packages 2>/dev/null | grep -q "package:$package"; then
|
||||
log "Blocked app detected: $package - Uninstalling..."
|
||||
pm uninstall "$package" 2>/dev/null && log "Uninstalled: $package" || log "Failed to uninstall: $package"
|
||||
fi
|
||||
done <"$BLOCKED_APPS_FILE"
|
||||
}
|
||||
|
||||
# Main monitoring loop - runs every 5 seconds for faster protection
|
||||
while true; do
|
||||
# ALWAYS protect module from UI disabling (even if guardian is "disabled" via ADB)
|
||||
# This ensures only ADB can control the guardian
|
||||
protect_module
|
||||
# ALWAYS protect module from UI disabling (even if guardian is "disabled" via ADB)
|
||||
# This ensures only ADB can control the guardian
|
||||
protect_module
|
||||
|
||||
if is_enabled; then
|
||||
protect_hosts
|
||||
check_blocked_apps
|
||||
fi
|
||||
if is_enabled; then
|
||||
protect_hosts
|
||||
check_blocked_apps
|
||||
fi
|
||||
|
||||
# Check every 5 seconds (faster response to disable attempts)
|
||||
sleep 5
|
||||
# Check every 5 seconds (faster response to disable attempts)
|
||||
sleep 5
|
||||
done &
|
||||
|
||||
log "Guardian service started (PID: $!)"
|
||||
|
||||
@ -16,32 +16,32 @@ MODULE_DEST="/data/adb/modules/android_guardian"
|
||||
|
||||
# Ensure android-tools (adb) is installed
|
||||
ensure_adb_installed() {
|
||||
if command -v adb &> /dev/null; then
|
||||
return 0
|
||||
fi
|
||||
if command -v adb &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "adb not found, installing android-tools..."
|
||||
log "adb not found, installing android-tools..."
|
||||
|
||||
if command -v pacman &> /dev/null; then
|
||||
sudo pacman -S --noconfirm android-tools || die "Failed to install android-tools"
|
||||
elif command -v apt-get &> /dev/null; then
|
||||
sudo apt-get update && sudo apt-get install -y adb || die "Failed to install adb"
|
||||
elif command -v dnf &> /dev/null; then
|
||||
sudo dnf install -y android-tools || die "Failed to install android-tools"
|
||||
else
|
||||
die "adb not found and could not determine package manager. Please install android-tools manually."
|
||||
fi
|
||||
if command -v pacman &>/dev/null; then
|
||||
sudo pacman -S --noconfirm android-tools || die "Failed to install android-tools"
|
||||
elif command -v apt-get &>/dev/null; then
|
||||
sudo apt-get update && sudo apt-get install -y adb || die "Failed to install adb"
|
||||
elif command -v dnf &>/dev/null; then
|
||||
sudo dnf install -y android-tools || die "Failed to install android-tools"
|
||||
else
|
||||
die "adb not found and could not determine package manager. Please install android-tools manually."
|
||||
fi
|
||||
|
||||
# Verify installation
|
||||
if ! command -v adb &> /dev/null; then
|
||||
die "adb installation failed"
|
||||
fi
|
||||
# Verify installation
|
||||
if ! command -v adb &>/dev/null; then
|
||||
die "adb installation failed"
|
||||
fi
|
||||
|
||||
log "android-tools installed successfully"
|
||||
log "android-tools installed successfully"
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
cat << EOF
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [COMMAND]
|
||||
|
||||
Commands:
|
||||
@ -81,239 +81,239 @@ WIRELESS_CONFIG="$HOME/.config/android_guardian_wireless"
|
||||
|
||||
# Discover Android devices on the network using mDNS
|
||||
discover_android_device() {
|
||||
local found_address=""
|
||||
local found_address=""
|
||||
|
||||
# Ensure avahi-browse is available
|
||||
if ! command -v avahi-browse &> /dev/null; then
|
||||
if command -v pacman &> /dev/null; then
|
||||
echo "Installing avahi for device discovery..." >&2
|
||||
sudo pacman -S --noconfirm avahi nss-mdns &> /dev/null || true
|
||||
sudo systemctl enable --now avahi-daemon &> /dev/null || true
|
||||
elif command -v apt-get &> /dev/null; then
|
||||
sudo apt-get install -y avahi-utils &> /dev/null || true
|
||||
fi
|
||||
fi
|
||||
# Ensure avahi-browse is available
|
||||
if ! command -v avahi-browse &>/dev/null; then
|
||||
if command -v pacman &>/dev/null; then
|
||||
echo "Installing avahi for device discovery..." >&2
|
||||
sudo pacman -S --noconfirm avahi nss-mdns &>/dev/null || true
|
||||
sudo systemctl enable --now avahi-daemon &>/dev/null || true
|
||||
elif command -v apt-get &>/dev/null; then
|
||||
sudo apt-get install -y avahi-utils &>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v avahi-browse &> /dev/null; then
|
||||
echo "Scanning for Android devices (5 seconds)..." >&2
|
||||
if command -v avahi-browse &>/dev/null; then
|
||||
echo "Scanning for Android devices (5 seconds)..." >&2
|
||||
|
||||
# Android wireless debugging advertises as _adb-tls-connect._tcp
|
||||
local discovery_result
|
||||
discovery_result=$(timeout 5 avahi-browse -rpt _adb-tls-connect._tcp 2> /dev/null | grep "^=" | head -1)
|
||||
# Android wireless debugging advertises as _adb-tls-connect._tcp
|
||||
local discovery_result
|
||||
discovery_result=$(timeout 5 avahi-browse -rpt _adb-tls-connect._tcp 2>/dev/null | grep "^=" | head -1)
|
||||
|
||||
if [[ -n $discovery_result ]]; then
|
||||
# Parse: =;eth0;IPv4;adb-...;_adb-tls-connect._tcp;local;hostname.local;192.168.x.x;port;...
|
||||
local ip port
|
||||
ip=$(echo "$discovery_result" | cut -d';' -f8)
|
||||
port=$(echo "$discovery_result" | cut -d';' -f9)
|
||||
if [[ -n $discovery_result ]]; then
|
||||
# Parse: =;eth0;IPv4;adb-...;_adb-tls-connect._tcp;local;hostname.local;192.168.x.x;port;...
|
||||
local ip port
|
||||
ip=$(echo "$discovery_result" | cut -d';' -f8)
|
||||
port=$(echo "$discovery_result" | cut -d';' -f9)
|
||||
|
||||
if [[ -n $ip && -n $port ]]; then
|
||||
found_address="$ip:$port"
|
||||
echo "✓ Found device: $found_address" >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ -n $ip && -n $port ]]; then
|
||||
found_address="$ip:$port"
|
||||
echo "✓ Found device: $found_address" >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback: try adb's mdns discovery
|
||||
if [[ -z $found_address ]]; then
|
||||
echo "Trying adb mdns discovery..." >&2
|
||||
# Fallback: try adb's mdns discovery
|
||||
if [[ -z $found_address ]]; then
|
||||
echo "Trying adb mdns discovery..." >&2
|
||||
|
||||
# adb can discover devices via mdns
|
||||
local mdns_result
|
||||
mdns_result=$(timeout 5 adb mdns services 2> /dev/null | grep -E "adb-tls-connect|_adb\._tcp" | head -1)
|
||||
# adb can discover devices via mdns
|
||||
local mdns_result
|
||||
mdns_result=$(timeout 5 adb mdns services 2>/dev/null | grep -E "adb-tls-connect|_adb\._tcp" | head -1)
|
||||
|
||||
if [[ -n $mdns_result ]]; then
|
||||
# Try to extract IP:port from the result
|
||||
local service_name
|
||||
service_name=$(echo "$mdns_result" | awk '{print $1}')
|
||||
if [[ -n $service_name ]]; then
|
||||
# Try connecting via service name
|
||||
echo "Found service: $service_name" >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [[ -n $mdns_result ]]; then
|
||||
# Try to extract IP:port from the result
|
||||
local service_name
|
||||
service_name=$(echo "$mdns_result" | awk '{print $1}')
|
||||
if [[ -n $service_name ]]; then
|
||||
# Try connecting via service name
|
||||
echo "Found service: $service_name" >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Return found address (or empty)
|
||||
echo "$found_address"
|
||||
# Return found address (or empty)
|
||||
echo "$found_address"
|
||||
}
|
||||
|
||||
# Pair with device over WiFi (Android 11+)
|
||||
cmd_pair() {
|
||||
ensure_adb_installed
|
||||
ensure_adb_installed
|
||||
|
||||
echo ""
|
||||
echo "=== Wireless ADB Pairing (Android 11+) ==="
|
||||
echo ""
|
||||
echo "On your phone:"
|
||||
echo " 1. Go to Settings > Developer Options > Wireless debugging"
|
||||
echo " 2. Enable Wireless debugging"
|
||||
echo " 3. Tap 'Pair device with pairing code'"
|
||||
echo " 4. Note the IP:port and pairing code shown"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "=== Wireless ADB Pairing (Android 11+) ==="
|
||||
echo ""
|
||||
echo "On your phone:"
|
||||
echo " 1. Go to Settings > Developer Options > Wireless debugging"
|
||||
echo " 2. Enable Wireless debugging"
|
||||
echo " 3. Tap 'Pair device with pairing code'"
|
||||
echo " 4. Note the IP:port and pairing code shown"
|
||||
echo ""
|
||||
|
||||
read -rp "Enter pairing IP:port (e.g., 192.168.1.100:37123): " pair_address
|
||||
read -rp "Enter pairing code: " pair_code
|
||||
read -rp "Enter pairing IP:port (e.g., 192.168.1.100:37123): " pair_address
|
||||
read -rp "Enter pairing code: " pair_code
|
||||
|
||||
if [[ -z $pair_address || -z $pair_code ]]; then
|
||||
die "Pairing address and code are required"
|
||||
fi
|
||||
if [[ -z $pair_address || -z $pair_code ]]; then
|
||||
die "Pairing address and code are required"
|
||||
fi
|
||||
|
||||
log "Pairing with device at $pair_address..."
|
||||
if adb pair "$pair_address" "$pair_code"; then
|
||||
echo ""
|
||||
echo "✓ Pairing successful!"
|
||||
echo ""
|
||||
echo "Now get the connection address:"
|
||||
echo " On phone: Wireless debugging screen shows IP:port under 'IP address & Port'"
|
||||
echo " (This is DIFFERENT from the pairing port)"
|
||||
echo ""
|
||||
read -rp "Enter connection IP:port (e.g., 192.168.1.100:41567): " connect_address
|
||||
log "Pairing with device at $pair_address..."
|
||||
if adb pair "$pair_address" "$pair_code"; then
|
||||
echo ""
|
||||
echo "✓ Pairing successful!"
|
||||
echo ""
|
||||
echo "Now get the connection address:"
|
||||
echo " On phone: Wireless debugging screen shows IP:port under 'IP address & Port'"
|
||||
echo " (This is DIFFERENT from the pairing port)"
|
||||
echo ""
|
||||
read -rp "Enter connection IP:port (e.g., 192.168.1.100:41567): " connect_address
|
||||
|
||||
if [[ -n $connect_address ]]; then
|
||||
# Save for future connections
|
||||
mkdir -p "$(dirname "$WIRELESS_CONFIG")"
|
||||
echo "$connect_address" > "$WIRELESS_CONFIG"
|
||||
log "Saved connection address for future use"
|
||||
if [[ -n $connect_address ]]; then
|
||||
# Save for future connections
|
||||
mkdir -p "$(dirname "$WIRELESS_CONFIG")"
|
||||
echo "$connect_address" >"$WIRELESS_CONFIG"
|
||||
log "Saved connection address for future use"
|
||||
|
||||
# Connect now
|
||||
cmd_connect
|
||||
fi
|
||||
else
|
||||
die "Pairing failed. Make sure the code is correct and you're on the same network."
|
||||
fi
|
||||
# Connect now
|
||||
cmd_connect
|
||||
fi
|
||||
else
|
||||
die "Pairing failed. Make sure the code is correct and you're on the same network."
|
||||
fi
|
||||
}
|
||||
|
||||
# Connect to already-paired device
|
||||
cmd_connect() {
|
||||
ensure_adb_installed
|
||||
ensure_adb_installed
|
||||
|
||||
local connect_address=""
|
||||
local connect_address=""
|
||||
|
||||
# Check for saved address
|
||||
if [[ -f $WIRELESS_CONFIG ]]; then
|
||||
connect_address=$(cat "$WIRELESS_CONFIG")
|
||||
log "Using saved address: $connect_address"
|
||||
fi
|
||||
# Check for saved address
|
||||
if [[ -f $WIRELESS_CONFIG ]]; then
|
||||
connect_address=$(cat "$WIRELESS_CONFIG")
|
||||
log "Using saved address: $connect_address"
|
||||
fi
|
||||
|
||||
# Try auto-discovery if no saved address
|
||||
if [[ -z $connect_address ]]; then
|
||||
echo ""
|
||||
log "Searching for Android devices on network..."
|
||||
connect_address=$(discover_android_device)
|
||||
fi
|
||||
# Try auto-discovery if no saved address
|
||||
if [[ -z $connect_address ]]; then
|
||||
echo ""
|
||||
log "Searching for Android devices on network..."
|
||||
connect_address=$(discover_android_device)
|
||||
fi
|
||||
|
||||
# Manual fallback
|
||||
if [[ -z $connect_address ]]; then
|
||||
echo ""
|
||||
echo "Auto-discovery failed. Enter address manually."
|
||||
echo "On phone: Settings > Developer Options > Wireless debugging"
|
||||
echo "Look for IP address & Port (NOT the pairing port)"
|
||||
echo ""
|
||||
read -rp "Enter connection IP:port (e.g., 192.168.1.100:41567): " connect_address
|
||||
# Manual fallback
|
||||
if [[ -z $connect_address ]]; then
|
||||
echo ""
|
||||
echo "Auto-discovery failed. Enter address manually."
|
||||
echo "On phone: Settings > Developer Options > Wireless debugging"
|
||||
echo "Look for IP address & Port (NOT the pairing port)"
|
||||
echo ""
|
||||
read -rp "Enter connection IP:port (e.g., 192.168.1.100:41567): " connect_address
|
||||
|
||||
if [[ -z $connect_address ]]; then
|
||||
die "Connection address is required"
|
||||
fi
|
||||
fi
|
||||
if [[ -z $connect_address ]]; then
|
||||
die "Connection address is required"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Save for future
|
||||
mkdir -p "$(dirname "$WIRELESS_CONFIG")"
|
||||
echo "$connect_address" > "$WIRELESS_CONFIG"
|
||||
# Save for future
|
||||
mkdir -p "$(dirname "$WIRELESS_CONFIG")"
|
||||
echo "$connect_address" >"$WIRELESS_CONFIG"
|
||||
|
||||
log "Connecting to $connect_address..."
|
||||
if adb connect "$connect_address" | grep -q "connected"; then
|
||||
echo ""
|
||||
echo "✓ Connected to device wirelessly!"
|
||||
echo ""
|
||||
log "Connecting to $connect_address..."
|
||||
if adb connect "$connect_address" | grep -q "connected"; then
|
||||
echo ""
|
||||
echo "✓ Connected to device wirelessly!"
|
||||
echo ""
|
||||
|
||||
# Verify connection
|
||||
if adb devices | grep -q "$connect_address"; then
|
||||
echo "Device ready. You can now run other commands."
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo "Connection failed. Possible issues:"
|
||||
echo " - Wireless debugging not enabled on phone"
|
||||
echo " - Phone and PC not on same WiFi network"
|
||||
echo " - Port changed (check Wireless debugging screen)"
|
||||
echo " - May need to pair first: $0 pair"
|
||||
echo ""
|
||||
# Clear saved config since it failed
|
||||
rm -f "$WIRELESS_CONFIG"
|
||||
exit 1
|
||||
fi
|
||||
# Verify connection
|
||||
if adb devices | grep -q "$connect_address"; then
|
||||
echo "Device ready. You can now run other commands."
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo "Connection failed. Possible issues:"
|
||||
echo " - Wireless debugging not enabled on phone"
|
||||
echo " - Phone and PC not on same WiFi network"
|
||||
echo " - Port changed (check Wireless debugging screen)"
|
||||
echo " - May need to pair first: $0 pair"
|
||||
echo ""
|
||||
# Clear saved config since it failed
|
||||
rm -f "$WIRELESS_CONFIG"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Disconnect wireless ADB
|
||||
cmd_disconnect() {
|
||||
ensure_adb_installed
|
||||
ensure_adb_installed
|
||||
|
||||
log "Disconnecting all wireless devices..."
|
||||
adb disconnect
|
||||
echo "✓ Disconnected"
|
||||
log "Disconnecting all wireless devices..."
|
||||
adb disconnect
|
||||
echo "✓ Disconnected"
|
||||
}
|
||||
|
||||
# Check device connection and root
|
||||
ensure_device_ready() {
|
||||
ensure_adb_installed
|
||||
ensure_adb_installed
|
||||
|
||||
# Check if any device is connected
|
||||
if ! adb devices | grep -qE "device$|:.*device$"; then
|
||||
echo ""
|
||||
echo "No device connected!"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " 1. Connect USB cable with debugging enabled"
|
||||
echo " 2. Use wireless: $0 pair (first time) or $0 connect"
|
||||
echo ""
|
||||
# Check if any device is connected
|
||||
if ! adb devices | grep -qE "device$|:.*device$"; then
|
||||
echo ""
|
||||
echo "No device connected!"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " 1. Connect USB cable with debugging enabled"
|
||||
echo " 2. Use wireless: $0 pair (first time) or $0 connect"
|
||||
echo ""
|
||||
|
||||
# Check if we have a saved wireless config
|
||||
if [[ -f $WIRELESS_CONFIG ]]; then
|
||||
read -rp "Try connecting to saved wireless device? [Y/n]: " try_wireless
|
||||
if [[ ${try_wireless,,} != "n" ]]; then
|
||||
cmd_connect
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
# Check if we have a saved wireless config
|
||||
if [[ -f $WIRELESS_CONFIG ]]; then
|
||||
read -rp "Try connecting to saved wireless device? [Y/n]: " try_wireless
|
||||
if [[ ${try_wireless,,} != "n" ]]; then
|
||||
cmd_connect
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
check_adb_device
|
||||
check_adb_root
|
||||
check_adb_device
|
||||
check_adb_root
|
||||
}
|
||||
|
||||
# Build the module zip
|
||||
build_module() {
|
||||
local tmp_dir="$WORK_DIR/guardian_module"
|
||||
local module_zip="$WORK_DIR/android_guardian.zip"
|
||||
local tmp_dir="$WORK_DIR/guardian_module"
|
||||
local module_zip="$WORK_DIR/android_guardian.zip"
|
||||
|
||||
echo "[BUILD] Building Android Guardian module..." >&2
|
||||
echo "[BUILD] Building Android Guardian module..." >&2
|
||||
|
||||
rm -rf "$tmp_dir"
|
||||
mkdir -p "$tmp_dir/system/etc"
|
||||
rm -rf "$tmp_dir"
|
||||
mkdir -p "$tmp_dir/system/etc"
|
||||
|
||||
# Copy module files
|
||||
cp "$GUARDIAN_MODULE_DIR/module.prop" "$tmp_dir/"
|
||||
cp "$GUARDIAN_MODULE_DIR/service.sh" "$tmp_dir/"
|
||||
cp "$GUARDIAN_MODULE_DIR/post-fs-data.sh" "$tmp_dir/"
|
||||
cp "$GUARDIAN_MODULE_DIR/uninstall.sh" "$tmp_dir/"
|
||||
# Copy module files
|
||||
cp "$GUARDIAN_MODULE_DIR/module.prop" "$tmp_dir/"
|
||||
cp "$GUARDIAN_MODULE_DIR/service.sh" "$tmp_dir/"
|
||||
cp "$GUARDIAN_MODULE_DIR/post-fs-data.sh" "$tmp_dir/"
|
||||
cp "$GUARDIAN_MODULE_DIR/uninstall.sh" "$tmp_dir/"
|
||||
|
||||
# Build hosts file
|
||||
local hosts_file="$tmp_dir/system/etc/hosts"
|
||||
if [[ -f /etc/hosts.stevenblack ]]; then
|
||||
echo "[BUILD] Using StevenBlack hosts cache..." >&2
|
||||
cp /etc/hosts.stevenblack "$hosts_file"
|
||||
elif [[ -f /etc/hosts ]]; then
|
||||
echo "[BUILD] Using /etc/hosts..." >&2
|
||||
cp /etc/hosts "$hosts_file"
|
||||
else
|
||||
die "No hosts file found"
|
||||
fi
|
||||
# Build hosts file
|
||||
local hosts_file="$tmp_dir/system/etc/hosts"
|
||||
if [[ -f /etc/hosts.stevenblack ]]; then
|
||||
echo "[BUILD] Using StevenBlack hosts cache..." >&2
|
||||
cp /etc/hosts.stevenblack "$hosts_file"
|
||||
elif [[ -f /etc/hosts ]]; then
|
||||
echo "[BUILD] Using /etc/hosts..." >&2
|
||||
cp /etc/hosts "$hosts_file"
|
||||
else
|
||||
die "No hosts file found"
|
||||
fi
|
||||
|
||||
# Append custom blocking entries
|
||||
cat >> "$hosts_file" << 'CUSTOM_EOF'
|
||||
# Append custom blocking entries
|
||||
cat >>"$hosts_file" <<'CUSTOM_EOF'
|
||||
|
||||
# ============================================
|
||||
# Custom blocking entries - Android Guardian
|
||||
@ -563,252 +563,252 @@ build_module() {
|
||||
0.0.0.0 www.dominos.com
|
||||
CUSTOM_EOF
|
||||
|
||||
local total_entries
|
||||
total_entries=$(grep -c "^0\.0\.0\.0 " "$hosts_file" || echo 0)
|
||||
echo "[BUILD] Hosts file contains $total_entries blocked domains" >&2
|
||||
local total_entries
|
||||
total_entries=$(grep -c "^0\.0\.0\.0 " "$hosts_file" || echo 0)
|
||||
echo "[BUILD] Hosts file contains $total_entries blocked domains" >&2
|
||||
|
||||
# Create zip
|
||||
(cd "$tmp_dir" && zip -r "$module_zip" . -x "*.DS_Store") > /dev/null
|
||||
# Create zip
|
||||
(cd "$tmp_dir" && zip -r "$module_zip" . -x "*.DS_Store") >/dev/null
|
||||
|
||||
echo "$module_zip"
|
||||
echo "$module_zip"
|
||||
}
|
||||
|
||||
# Install/update the guardian module
|
||||
cmd_install() {
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
local module_zip
|
||||
module_zip=$(build_module)
|
||||
local module_zip
|
||||
module_zip=$(build_module)
|
||||
|
||||
log "Pushing module to device..."
|
||||
adb push "$module_zip" /sdcard/android_guardian.zip || die "Failed to push module"
|
||||
log "Pushing module to device..."
|
||||
adb push "$module_zip" /sdcard/android_guardian.zip || die "Failed to push module"
|
||||
|
||||
log "Installing module..."
|
||||
adb shell "su -c 'mkdir -p $MODULE_DEST'" || die "Failed to create module directory"
|
||||
adb shell "su -c 'cd $MODULE_DEST && unzip -o /sdcard/android_guardian.zip'" || die "Failed to extract module"
|
||||
adb shell "su -c 'chmod 755 $MODULE_DEST/*.sh'"
|
||||
adb shell "su -c 'rm /sdcard/android_guardian.zip'"
|
||||
log "Installing module..."
|
||||
adb shell "su -c 'mkdir -p $MODULE_DEST'" || die "Failed to create module directory"
|
||||
adb shell "su -c 'cd $MODULE_DEST && unzip -o /sdcard/android_guardian.zip'" || die "Failed to extract module"
|
||||
adb shell "su -c 'chmod 755 $MODULE_DEST/*.sh'"
|
||||
adb shell "su -c 'rm /sdcard/android_guardian.zip'"
|
||||
|
||||
# Set up guardian data directory
|
||||
log "Setting up guardian data..."
|
||||
adb shell "su -c 'mkdir -p $GUARDIAN_DATA_DIR'"
|
||||
adb shell "su -c 'echo ENABLED > $GUARDIAN_DATA_DIR/control'"
|
||||
# Set up guardian data directory
|
||||
log "Setting up guardian data..."
|
||||
adb shell "su -c 'mkdir -p $GUARDIAN_DATA_DIR'"
|
||||
adb shell "su -c 'echo ENABLED > $GUARDIAN_DATA_DIR/control'"
|
||||
|
||||
# Copy blocked apps list
|
||||
adb push "$GUARDIAN_MODULE_DIR/blocked_apps.txt" /sdcard/blocked_apps.txt || die "Failed to push blocked apps list"
|
||||
adb shell "su -c 'cp /sdcard/blocked_apps.txt $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||
adb shell "su -c 'rm /sdcard/blocked_apps.txt'"
|
||||
# Copy blocked apps list
|
||||
adb push "$GUARDIAN_MODULE_DIR/blocked_apps.txt" /sdcard/blocked_apps.txt || die "Failed to push blocked apps list"
|
||||
adb shell "su -c 'cp /sdcard/blocked_apps.txt $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||
adb shell "su -c 'rm /sdcard/blocked_apps.txt'"
|
||||
|
||||
# Create hosts backup for tamper protection
|
||||
adb shell "su -c 'cp $MODULE_DEST/system/etc/hosts $GUARDIAN_DATA_DIR/hosts.backup'"
|
||||
# Create hosts backup for tamper protection
|
||||
adb shell "su -c 'cp $MODULE_DEST/system/etc/hosts $GUARDIAN_DATA_DIR/hosts.backup'"
|
||||
|
||||
# Immediately uninstall any currently installed blocked apps
|
||||
log "Checking for blocked apps to remove..."
|
||||
uninstall_blocked_apps
|
||||
# Immediately uninstall any currently installed blocked apps
|
||||
log "Checking for blocked apps to remove..."
|
||||
uninstall_blocked_apps
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " ✓ Android Guardian installed!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Features enabled:"
|
||||
echo " • Hosts-based ad/tracker blocking"
|
||||
echo " • App installation blocking"
|
||||
echo " • Tamper protection"
|
||||
echo ""
|
||||
echo "⚠️ This can ONLY be controlled via ADB:"
|
||||
echo " Disable: $0 disable"
|
||||
echo " Enable: $0 enable"
|
||||
echo " Status: $0 status"
|
||||
echo ""
|
||||
echo "Reboot your device to activate the module."
|
||||
echo ""
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo " ✓ Android Guardian installed!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Features enabled:"
|
||||
echo " • Hosts-based ad/tracker blocking"
|
||||
echo " • App installation blocking"
|
||||
echo " • Tamper protection"
|
||||
echo ""
|
||||
echo "⚠️ This can ONLY be controlled via ADB:"
|
||||
echo " Disable: $0 disable"
|
||||
echo " Enable: $0 enable"
|
||||
echo " Status: $0 status"
|
||||
echo ""
|
||||
echo "Reboot your device to activate the module."
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Uninstall currently installed blocked apps
|
||||
uninstall_blocked_apps() {
|
||||
local blocked_apps
|
||||
blocked_apps=$(grep -v '^#' "$GUARDIAN_MODULE_DIR/blocked_apps.txt" | grep -v '^$' || true)
|
||||
local blocked_apps
|
||||
blocked_apps=$(grep -v '^#' "$GUARDIAN_MODULE_DIR/blocked_apps.txt" | grep -v '^$' || true)
|
||||
|
||||
for package in $blocked_apps; do
|
||||
if adb shell "pm list packages" 2> /dev/null | grep -q "package:$package"; then
|
||||
log "Uninstalling blocked app: $package"
|
||||
adb shell "pm uninstall $package" 2> /dev/null || true
|
||||
fi
|
||||
done
|
||||
for package in $blocked_apps; do
|
||||
if adb shell "pm list packages" 2>/dev/null | grep -q "package:$package"; then
|
||||
log "Uninstalling blocked app: $package"
|
||||
adb shell "pm uninstall $package" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Show status
|
||||
cmd_status() {
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
echo ""
|
||||
echo "=== Android Guardian Status ==="
|
||||
echo ""
|
||||
echo ""
|
||||
echo "=== Android Guardian Status ==="
|
||||
echo ""
|
||||
|
||||
# Check if module is installed
|
||||
if adb shell "su -c 'test -d $MODULE_DEST'" 2> /dev/null; then
|
||||
echo "Module: INSTALLED"
|
||||
else
|
||||
echo "Module: NOT INSTALLED"
|
||||
return
|
||||
fi
|
||||
# Check if module is installed
|
||||
if adb shell "su -c 'test -d $MODULE_DEST'" 2>/dev/null; then
|
||||
echo "Module: INSTALLED"
|
||||
else
|
||||
echo "Module: NOT INSTALLED"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check control status
|
||||
local status
|
||||
status=$(adb shell "su -c 'cat $GUARDIAN_DATA_DIR/control 2>/dev/null || echo UNKNOWN'" | tr -d '\r')
|
||||
echo "Status: $status"
|
||||
# Check control status
|
||||
local status
|
||||
status=$(adb shell "su -c 'cat $GUARDIAN_DATA_DIR/control 2>/dev/null || echo UNKNOWN'" | tr -d '\r')
|
||||
echo "Status: $status"
|
||||
|
||||
# Check if module is "disabled" in Magisk UI (should be auto-fixed by watchdog)
|
||||
local magisk_disabled
|
||||
if adb shell "su -c 'test -f $MODULE_DEST/disable'" 2> /dev/null; then
|
||||
magisk_disabled="YES (watchdog should fix this)"
|
||||
else
|
||||
magisk_disabled="No"
|
||||
fi
|
||||
echo "Magisk UI disabled: $magisk_disabled"
|
||||
# Check if module is "disabled" in Magisk UI (should be auto-fixed by watchdog)
|
||||
local magisk_disabled
|
||||
if adb shell "su -c 'test -f $MODULE_DEST/disable'" 2>/dev/null; then
|
||||
magisk_disabled="YES (watchdog should fix this)"
|
||||
else
|
||||
magisk_disabled="No"
|
||||
fi
|
||||
echo "Magisk UI disabled: $magisk_disabled"
|
||||
|
||||
# Check if watchdog is running
|
||||
local watchdog_running
|
||||
watchdog_running=$(adb shell "su -c 'pgrep -f watchdog.sh 2>/dev/null | wc -l'" | tr -d '\r')
|
||||
if [ "$watchdog_running" -gt 0 ] 2> /dev/null; then
|
||||
echo "Watchdog: RUNNING ($watchdog_running processes)"
|
||||
else
|
||||
echo "Watchdog: NOT RUNNING (reboot phone to start)"
|
||||
fi
|
||||
# Check if watchdog is running
|
||||
local watchdog_running
|
||||
watchdog_running=$(adb shell "su -c 'pgrep -f watchdog.sh 2>/dev/null | wc -l'" | tr -d '\r')
|
||||
if [ "$watchdog_running" -gt 0 ] 2>/dev/null; then
|
||||
echo "Watchdog: RUNNING ($watchdog_running processes)"
|
||||
else
|
||||
echo "Watchdog: NOT RUNNING (reboot phone to start)"
|
||||
fi
|
||||
|
||||
# Check hosts file
|
||||
local hosts_entries
|
||||
hosts_entries=$(adb shell "su -c 'grep -c \"^0.0.0.0\" /system/etc/hosts 2>/dev/null || echo 0'" | tr -d '\r')
|
||||
echo "Blocked domains: $hosts_entries"
|
||||
# Check hosts file
|
||||
local hosts_entries
|
||||
hosts_entries=$(adb shell "su -c 'grep -c \"^0.0.0.0\" /system/etc/hosts 2>/dev/null || echo 0'" | tr -d '\r')
|
||||
echo "Blocked domains: $hosts_entries"
|
||||
|
||||
# Check blocked apps count
|
||||
local blocked_count
|
||||
blocked_count=$(adb shell "su -c 'grep -v \"^#\" $GUARDIAN_DATA_DIR/blocked_apps.txt 2>/dev/null | grep -v \"^$\" | wc -l || echo 0'" | tr -d '\r')
|
||||
echo "Blocked app rules: $blocked_count packages"
|
||||
# Check blocked apps count
|
||||
local blocked_count
|
||||
blocked_count=$(adb shell "su -c 'grep -v \"^#\" $GUARDIAN_DATA_DIR/blocked_apps.txt 2>/dev/null | grep -v \"^$\" | wc -l || echo 0'" | tr -d '\r')
|
||||
echo "Blocked app rules: $blocked_count packages"
|
||||
|
||||
echo ""
|
||||
echo "Protection: Module cannot be disabled from Magisk UI"
|
||||
echo " Only controllable via: $0 disable/enable"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "Protection: Module cannot be disabled from Magisk UI"
|
||||
echo " Only controllable via: $0 disable/enable"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Disable guardian
|
||||
cmd_disable() {
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
log "Disabling Android Guardian..."
|
||||
adb shell "su -c 'echo DISABLED > $GUARDIAN_DATA_DIR/control'" || die "Failed to disable guardian"
|
||||
log "Disabling Android Guardian..."
|
||||
adb shell "su -c 'echo DISABLED > $GUARDIAN_DATA_DIR/control'" || die "Failed to disable guardian"
|
||||
|
||||
echo ""
|
||||
echo "✓ Guardian DISABLED"
|
||||
echo " Hosts blocking still active until reboot"
|
||||
echo " App blocking service paused"
|
||||
echo ""
|
||||
echo "To re-enable: $0 enable"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "✓ Guardian DISABLED"
|
||||
echo " Hosts blocking still active until reboot"
|
||||
echo " App blocking service paused"
|
||||
echo ""
|
||||
echo "To re-enable: $0 enable"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Enable guardian
|
||||
cmd_enable() {
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
log "Enabling Android Guardian..."
|
||||
adb shell "su -c 'echo ENABLED > $GUARDIAN_DATA_DIR/control'" || die "Failed to enable guardian"
|
||||
log "Enabling Android Guardian..."
|
||||
adb shell "su -c 'echo ENABLED > $GUARDIAN_DATA_DIR/control'" || die "Failed to enable guardian"
|
||||
|
||||
echo ""
|
||||
echo "✓ Guardian ENABLED"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "✓ Guardian ENABLED"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Uninstall module
|
||||
cmd_uninstall() {
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
# Check if disabled first
|
||||
local status
|
||||
status=$(adb shell "su -c 'cat $GUARDIAN_DATA_DIR/control 2>/dev/null || echo ENABLED'" | tr -d '\r')
|
||||
# Check if disabled first
|
||||
local status
|
||||
status=$(adb shell "su -c 'cat $GUARDIAN_DATA_DIR/control 2>/dev/null || echo ENABLED'" | tr -d '\r')
|
||||
|
||||
if [[ $status != "DISABLED" ]]; then
|
||||
echo ""
|
||||
echo "⚠️ Guardian must be disabled before uninstalling!"
|
||||
echo " Run: $0 disable"
|
||||
echo " Then: $0 uninstall"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
if [[ $status != "DISABLED" ]]; then
|
||||
echo ""
|
||||
echo "⚠️ Guardian must be disabled before uninstalling!"
|
||||
echo " Run: $0 disable"
|
||||
echo " Then: $0 uninstall"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Removing Android Guardian..."
|
||||
adb shell "su -c 'rm -rf $MODULE_DEST'"
|
||||
adb shell "su -c 'rm -rf $GUARDIAN_DATA_DIR'"
|
||||
log "Removing Android Guardian..."
|
||||
adb shell "su -c 'rm -rf $MODULE_DEST'"
|
||||
adb shell "su -c 'rm -rf $GUARDIAN_DATA_DIR'"
|
||||
|
||||
echo ""
|
||||
echo "✓ Guardian uninstalled"
|
||||
echo " Reboot to remove hosts blocking"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "✓ Guardian uninstalled"
|
||||
echo " Reboot to remove hosts blocking"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Show logs
|
||||
cmd_logs() {
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
echo "=== Guardian Logs ==="
|
||||
adb shell "su -c 'cat $GUARDIAN_DATA_DIR/guardian.log 2>/dev/null || echo \"No logs yet\"'"
|
||||
echo "=== Guardian Logs ==="
|
||||
adb shell "su -c 'cat $GUARDIAN_DATA_DIR/guardian.log 2>/dev/null || echo \"No logs yet\"'"
|
||||
}
|
||||
|
||||
# Block an app
|
||||
cmd_block_app() {
|
||||
local package="${1:-}"
|
||||
local package="${1:-}"
|
||||
|
||||
if [[ -z $package ]]; then
|
||||
echo "Usage: $0 block-app <package.name>"
|
||||
echo "Example: $0 block-app com.ubercab.eats"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z $package ]]; then
|
||||
echo "Usage: $0 block-app <package.name>"
|
||||
echo "Example: $0 block-app com.ubercab.eats"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
log "Adding $package to block list..."
|
||||
adb shell "su -c 'echo \"$package\" >> $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||
log "Adding $package to block list..."
|
||||
adb shell "su -c 'echo \"$package\" >> $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||
|
||||
# Also add to local file
|
||||
echo "$package" >> "$GUARDIAN_MODULE_DIR/blocked_apps.txt"
|
||||
# Also add to local file
|
||||
echo "$package" >>"$GUARDIAN_MODULE_DIR/blocked_apps.txt"
|
||||
|
||||
# Try to uninstall if currently installed
|
||||
if adb shell "pm list packages" 2> /dev/null | grep -q "package:$package"; then
|
||||
log "Uninstalling $package..."
|
||||
adb shell "pm uninstall $package" 2> /dev/null || true
|
||||
fi
|
||||
# Try to uninstall if currently installed
|
||||
if adb shell "pm list packages" 2>/dev/null | grep -q "package:$package"; then
|
||||
log "Uninstalling $package..."
|
||||
adb shell "pm uninstall $package" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "✓ $package added to block list"
|
||||
echo "✓ $package added to block list"
|
||||
}
|
||||
|
||||
# Unblock an app
|
||||
cmd_unblock_app() {
|
||||
local package="${1:-}"
|
||||
local package="${1:-}"
|
||||
|
||||
if [[ -z $package ]]; then
|
||||
echo "Usage: $0 unblock-app <package.name>"
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z $package ]]; then
|
||||
echo "Usage: $0 unblock-app <package.name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
log "Removing $package from block list..."
|
||||
adb shell "su -c 'grep -v \"^$package\$\" $GUARDIAN_DATA_DIR/blocked_apps.txt > $GUARDIAN_DATA_DIR/blocked_apps.tmp && mv $GUARDIAN_DATA_DIR/blocked_apps.tmp $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||
log "Removing $package from block list..."
|
||||
adb shell "su -c 'grep -v \"^$package\$\" $GUARDIAN_DATA_DIR/blocked_apps.txt > $GUARDIAN_DATA_DIR/blocked_apps.tmp && mv $GUARDIAN_DATA_DIR/blocked_apps.tmp $GUARDIAN_DATA_DIR/blocked_apps.txt'"
|
||||
|
||||
# Also remove from local file
|
||||
grep -v "^$package$" "$GUARDIAN_MODULE_DIR/blocked_apps.txt" > "$GUARDIAN_MODULE_DIR/blocked_apps.tmp" && mv "$GUARDIAN_MODULE_DIR/blocked_apps.tmp" "$GUARDIAN_MODULE_DIR/blocked_apps.txt"
|
||||
# Also remove from local file
|
||||
grep -v "^$package$" "$GUARDIAN_MODULE_DIR/blocked_apps.txt" >"$GUARDIAN_MODULE_DIR/blocked_apps.tmp" && mv "$GUARDIAN_MODULE_DIR/blocked_apps.tmp" "$GUARDIAN_MODULE_DIR/blocked_apps.txt"
|
||||
|
||||
echo "✓ $package removed from block list"
|
||||
echo "✓ $package removed from block list"
|
||||
}
|
||||
|
||||
# List blocked apps
|
||||
cmd_list_blocked() {
|
||||
ensure_device_ready
|
||||
ensure_device_ready
|
||||
|
||||
echo "=== Blocked Apps ==="
|
||||
adb shell "su -c 'cat $GUARDIAN_DATA_DIR/blocked_apps.txt 2>/dev/null'" | grep -v "^#" | grep -v "^$" || echo "No blocked apps"
|
||||
echo "=== Blocked Apps ==="
|
||||
adb shell "su -c 'cat $GUARDIAN_DATA_DIR/blocked_apps.txt 2>/dev/null'" | grep -v "^#" | grep -v "^$" || echo "No blocked apps"
|
||||
}
|
||||
|
||||
# Main
|
||||
@ -819,48 +819,48 @@ COMMAND="${1:-install}"
|
||||
shift || true
|
||||
|
||||
case "$COMMAND" in
|
||||
install)
|
||||
cmd_install
|
||||
;;
|
||||
status)
|
||||
cmd_status
|
||||
;;
|
||||
disable)
|
||||
cmd_disable
|
||||
;;
|
||||
enable)
|
||||
cmd_enable
|
||||
;;
|
||||
uninstall)
|
||||
cmd_uninstall
|
||||
;;
|
||||
logs)
|
||||
cmd_logs
|
||||
;;
|
||||
block-app)
|
||||
cmd_block_app "$@"
|
||||
;;
|
||||
unblock-app)
|
||||
cmd_unblock_app "$@"
|
||||
;;
|
||||
list-blocked)
|
||||
cmd_list_blocked
|
||||
;;
|
||||
pair)
|
||||
cmd_pair
|
||||
;;
|
||||
connect)
|
||||
cmd_connect
|
||||
;;
|
||||
disconnect)
|
||||
cmd_disconnect
|
||||
;;
|
||||
-h | --help | help)
|
||||
show_usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $COMMAND"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
install)
|
||||
cmd_install
|
||||
;;
|
||||
status)
|
||||
cmd_status
|
||||
;;
|
||||
disable)
|
||||
cmd_disable
|
||||
;;
|
||||
enable)
|
||||
cmd_enable
|
||||
;;
|
||||
uninstall)
|
||||
cmd_uninstall
|
||||
;;
|
||||
logs)
|
||||
cmd_logs
|
||||
;;
|
||||
block-app)
|
||||
cmd_block_app "$@"
|
||||
;;
|
||||
unblock-app)
|
||||
cmd_unblock_app "$@"
|
||||
;;
|
||||
list-blocked)
|
||||
cmd_list_blocked
|
||||
;;
|
||||
pair)
|
||||
cmd_pair
|
||||
;;
|
||||
connect)
|
||||
cmd_connect
|
||||
;;
|
||||
disconnect)
|
||||
cmd_disconnect
|
||||
;;
|
||||
-h | --help | help)
|
||||
show_usage
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command: $COMMAND"
|
||||
show_usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -13,8 +13,8 @@ echo "WARNING: This may take a very long time (fetching ~2500 gminy)"
|
||||
echo
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
|
||||
@ -11,8 +11,8 @@ echo "=== Polish Powiaty Anki Generator ==="
|
||||
echo
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
|
||||
@ -11,8 +11,8 @@ echo "=== Warsaw Bridges Anki Generator ==="
|
||||
echo
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
|
||||
@ -12,8 +12,8 @@ echo
|
||||
|
||||
# Create virtual environment if it doesn't exist
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
# Activate virtual environment
|
||||
|
||||
@ -11,8 +11,8 @@ echo "=== Warsaw Landmarks Anki Generator ==="
|
||||
echo
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
|
||||
@ -11,8 +11,8 @@ echo "=== Warsaw Metro Stations Anki Generator ==="
|
||||
echo
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
|
||||
@ -11,8 +11,8 @@ echo "=== Warsaw Osiedla Anki Generator ==="
|
||||
echo
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
|
||||
@ -11,8 +11,8 @@ echo "=== Warsaw Streets Anki Generator ==="
|
||||
echo
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
|
||||
echo "Activating virtual environment..."
|
||||
|
||||
14
setup.sh
Executable file
14
setup.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# Post-clone setup script for testsAndMisc repository.
|
||||
# Run once after cloning: ./setup.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(git rev-parse --show-toplevel)"
|
||||
cd "$repo_root"
|
||||
|
||||
printf 'Configuring git hooks path...\n'
|
||||
git config core.hooksPath linux_configuration/.githooks
|
||||
printf ' ✓ core.hooksPath set to linux_configuration/.githooks\n'
|
||||
|
||||
printf 'Setup complete.\n'
|
||||
Loading…
Reference in New Issue
Block a user