mirror of
https://github.com/kuhyx/scripts.git
synced 2026-11-11 14:27:41 +01:00
feat: virtualbox remote control scripts
This commit is contained in:
parent
03bf1885ae
commit
f830f61392
416
scripts/control_from_mobile.sh
Executable file
416
scripts/control_from_mobile.sh
Executable file
@ -0,0 +1,416 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
SCRIPT_NAME="$(basename "$0")"
|
||||||
|
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/control-from-mobile"
|
||||||
|
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/control-from-mobile"
|
||||||
|
PASSWORD_FILE="$CONFIG_DIR/vnc.pass"
|
||||||
|
ENV_FILE="$CONFIG_DIR/env"
|
||||||
|
RUNNER_FILE="$CONFIG_DIR/start-x11vnc.sh"
|
||||||
|
SERVICE_NAME="control-from-mobile.service"
|
||||||
|
SYSTEMD_USER_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
|
||||||
|
SERVICE_FILE="$SYSTEMD_USER_DIR/$SERVICE_NAME"
|
||||||
|
DEFAULT_DISPLAY="${DISPLAY:-:0}"
|
||||||
|
DEFAULT_PORT=5901
|
||||||
|
DEFAULT_BIND_ADDR="0.0.0.0"
|
||||||
|
readonly SCRIPT_NAME CONFIG_DIR STATE_DIR PASSWORD_FILE ENV_FILE RUNNER_FILE SERVICE_NAME SYSTEMD_USER_DIR SERVICE_FILE DEFAULT_DISPLAY DEFAULT_PORT DEFAULT_BIND_ADDR
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: control_from_mobile.sh <command> [options]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
setup [--force-password] Install dependencies, create configs, and write the systemd user service.
|
||||||
|
start Start the VNC bridge (via systemd user unit when available).
|
||||||
|
stop Stop the bridge.
|
||||||
|
restart Restart the bridge.
|
||||||
|
status Show whether the bridge service is running.
|
||||||
|
enable Enable the service so it starts after login.
|
||||||
|
disable Disable automatic start after login.
|
||||||
|
info Show connection details and Android app suggestions.
|
||||||
|
uninstall Stop the service and remove generated files (keeps password unless --purge).
|
||||||
|
help Show this message.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--force-password Regenerate the VNC password during setup.
|
||||||
|
--purge Delete the stored VNC password during uninstall.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
./control_from_mobile.sh setup
|
||||||
|
./control_from_mobile.sh start
|
||||||
|
./control_from_mobile.sh info
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
printf '[%s] %s\n' "$SCRIPT_NAME" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
printf '[%s] %s\n' "$SCRIPT_NAME" "$*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
warn "$*"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
require_non_root() {
|
||||||
|
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
|
||||||
|
die "Run this script as a regular desktop user, not root."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_yes_no() {
|
||||||
|
local prompt="$1"
|
||||||
|
local reply
|
||||||
|
read -r -p "$prompt [y/N]: " reply
|
||||||
|
case "$reply" in
|
||||||
|
[Yy][Ee][Ss]|[Yy]) return 0 ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_directories() {
|
||||||
|
mkdir -p "$CONFIG_DIR" "$STATE_DIR" "$SYSTEMD_USER_DIR"
|
||||||
|
chmod 700 "$CONFIG_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
missing_commands() {
|
||||||
|
local missing=()
|
||||||
|
for cmd in "$@"; do
|
||||||
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||||
|
missing+=("$cmd")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
printf '%s\n' "${missing[@]-}"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_dependencies() {
|
||||||
|
if ! command -v systemctl >/dev/null 2>&1; then
|
||||||
|
die "systemctl not found. Install systemd before running this script."
|
||||||
|
fi
|
||||||
|
|
||||||
|
local required=(x11vnc qrencode ssh)
|
||||||
|
local needed=()
|
||||||
|
mapfile -t needed < <(missing_commands "${required[@]}")
|
||||||
|
if (( ${#needed[@]} == 0 )); then
|
||||||
|
log "All required packages (${required[*]}) are present."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v pacman >/dev/null 2>&1; then
|
||||||
|
log "Installing missing packages: ${needed[*]}"
|
||||||
|
sudo pacman -S --needed --noconfirm "${needed[@]}"
|
||||||
|
else
|
||||||
|
die "Missing commands (${needed[*]}). Install them manually and rerun setup."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_password_file() {
|
||||||
|
local force=${1:-0}
|
||||||
|
if [[ -f "$PASSWORD_FILE" && "$force" -ne 1 ]];
|
||||||
|
then
|
||||||
|
log "Using existing VNC password file at $PASSWORD_FILE"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$PASSWORD_FILE" ]]; then
|
||||||
|
if ! prompt_yes_no "Regenerate the stored VNC password?"; then
|
||||||
|
log "Keeping existing password."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local password confirm generated=0
|
||||||
|
read -rsp "Enter VNC password (leave blank to auto-generate): " password
|
||||||
|
printf '\n'
|
||||||
|
if [[ -z "$password" ]]; then
|
||||||
|
generated=1
|
||||||
|
password=$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 8)
|
||||||
|
log "Generated VNC password: $password"
|
||||||
|
else
|
||||||
|
read -rsp "Confirm password: " confirm
|
||||||
|
printf '\n'
|
||||||
|
if [[ "$password" != "$confirm" ]]; then
|
||||||
|
die "Passwords do not match."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tmp
|
||||||
|
tmp=$(mktemp)
|
||||||
|
x11vnc -storepasswd "$password" "$tmp" >/dev/null
|
||||||
|
install -m 600 "$tmp" "$PASSWORD_FILE"
|
||||||
|
rm -f "$tmp"
|
||||||
|
|
||||||
|
if (( generated == 0 )); then
|
||||||
|
log "Password stored securely at $PASSWORD_FILE (hashed)."
|
||||||
|
else
|
||||||
|
log "Please write down the generated password; it will be needed on your Android device."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_env_file() {
|
||||||
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
cat >"$ENV_FILE" <<EOF
|
||||||
|
# control-from-mobile configuration
|
||||||
|
# Adjust these values if needed and rerun: systemctl --user restart $SERVICE_NAME
|
||||||
|
X11_DISPLAY="$DEFAULT_DISPLAY"
|
||||||
|
VNC_PORT="$DEFAULT_PORT"
|
||||||
|
# Use 127.0.0.1 to force SSH tunnel-only access, or 0.0.0.0 to expose on LAN.
|
||||||
|
VNC_BIND_ADDR="$DEFAULT_BIND_ADDR"
|
||||||
|
EOF
|
||||||
|
chmod 600 "$ENV_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_runner_script() {
|
||||||
|
cat >"$RUNNER_FILE" <<'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
|
||||||
|
CONFIG_DIR="$(dirname "$(readlink -f "$0")")"
|
||||||
|
PASSWORD_FILE="$CONFIG_DIR/vnc.pass"
|
||||||
|
ENV_FILE="$CONFIG_DIR/env"
|
||||||
|
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/control-from-mobile"
|
||||||
|
mkdir -p "$STATE_DIR"
|
||||||
|
|
||||||
|
if [[ ! -f "$PASSWORD_FILE" ]]; then
|
||||||
|
echo "Missing VNC password file at $PASSWORD_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$ENV_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
X11_DISPLAY="${X11_DISPLAY:-${DISPLAY:-:0}}"
|
||||||
|
VNC_PORT="${VNC_PORT:-5901}"
|
||||||
|
VNC_BIND_ADDR="${VNC_BIND_ADDR:-0.0.0.0}"
|
||||||
|
|
||||||
|
LOG_FILE="$STATE_DIR/x11vnc.log"
|
||||||
|
exec /usr/bin/x11vnc \
|
||||||
|
-display "$X11_DISPLAY" \
|
||||||
|
-rfbport "$VNC_PORT" \
|
||||||
|
-listen "$VNC_BIND_ADDR" \
|
||||||
|
-forever \
|
||||||
|
-shared \
|
||||||
|
-auth guess \
|
||||||
|
-rfbauth "$PASSWORD_FILE" \
|
||||||
|
-noxdamage \
|
||||||
|
-repeat \
|
||||||
|
-ncache 10 \
|
||||||
|
-ncache_cr \
|
||||||
|
-o "$LOG_FILE"
|
||||||
|
EOF
|
||||||
|
chmod 700 "$RUNNER_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_service_file() {
|
||||||
|
cat >"$SERVICE_FILE" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Expose X11 desktop over VNC for Android control
|
||||||
|
After=graphical-session.target
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
EnvironmentFile=$ENV_FILE
|
||||||
|
ExecStart=$RUNNER_FILE
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=2
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
reload_user_daemon() {
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_service_present() {
|
||||||
|
if [[ ! -f "$SERVICE_FILE" || ! -x "$RUNNER_FILE" ]]; then
|
||||||
|
die "Service files missing. Run: $SCRIPT_NAME setup"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
ensure_service_present
|
||||||
|
systemctl --user start "$SERVICE_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
systemctl --user stop "$SERVICE_NAME" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
status_service() {
|
||||||
|
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
||||||
|
log "Service is active."
|
||||||
|
else
|
||||||
|
log "Service is inactive."
|
||||||
|
fi
|
||||||
|
systemctl --user status "$SERVICE_NAME" --no-pager || true
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_service() {
|
||||||
|
ensure_service_present
|
||||||
|
systemctl --user enable "$SERVICE_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_service() {
|
||||||
|
systemctl --user disable "$SERVICE_NAME" || true
|
||||||
|
}
|
||||||
|
|
||||||
|
show_info() {
|
||||||
|
ensure_service_present
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
|
||||||
|
local port="${VNC_PORT:-$DEFAULT_PORT}"
|
||||||
|
local bind_addr="${VNC_BIND_ADDR:-$DEFAULT_BIND_ADDR}"
|
||||||
|
local display="${X11_DISPLAY:-$DEFAULT_DISPLAY}"
|
||||||
|
|
||||||
|
local is_active="inactive"
|
||||||
|
if systemctl --user is-active --quiet "$SERVICE_NAME"; then
|
||||||
|
is_active="active"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Service status: $is_active"
|
||||||
|
log "Display: $display"
|
||||||
|
log "Listening address: $bind_addr"
|
||||||
|
log "VNC port: $port"
|
||||||
|
log "Password file: $PASSWORD_FILE"
|
||||||
|
|
||||||
|
local -a ip_list=()
|
||||||
|
if command -v hostname >/dev/null 2>&1; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
ip_list+=("$line")
|
||||||
|
done < <(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]' || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ${#ip_list[@]} > 0 )); then
|
||||||
|
log "Detected LAN IPs:"
|
||||||
|
for ip in "${ip_list[@]}"; do
|
||||||
|
printf ' - %s\n' "$ip"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
warn "Could not detect LAN IPs."
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '\nRecommended Android clients (FOSS):\n'
|
||||||
|
printf ' • bVNC (available on F-Droid) — supports full control.\n'
|
||||||
|
printf ' • Termux + OpenSSH for establishing an SSH tunnel when exposing only on 127.0.0.1.\n'
|
||||||
|
printf '\nConnect via VNC:\n'
|
||||||
|
printf ' Host: <your-ip>\n Port: %s\n Password: <stored during setup>\n' "$port"
|
||||||
|
|
||||||
|
local qr_host
|
||||||
|
if (( ${#ip_list[@]} > 0 )); then
|
||||||
|
qr_host="${ip_list[0]}"
|
||||||
|
else
|
||||||
|
qr_host="$bind_addr"
|
||||||
|
if [[ "$qr_host" == "0.0.0.0" || "$qr_host" == "::" ]]; then
|
||||||
|
qr_host="127.0.0.1"
|
||||||
|
fi
|
||||||
|
warn "Using fallback host $qr_host for QR code; replace with an accessible IP if needed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v qrencode >/dev/null 2>&1; then
|
||||||
|
printf '\nConnection QR (vnc://%s:%s):\n' "$qr_host" "$port"
|
||||||
|
qrencode -o - "vnc://$qr_host:$port" -t ASCII || true
|
||||||
|
else
|
||||||
|
warn "qrencode not found; reinstall qrencode to get QR codes."
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '\nFor encrypted access outside your LAN, use Termux on Android:\n'
|
||||||
|
printf ' ssh -L %s:localhost:%s <user>@<public-ip>\n' "$port" "$port"
|
||||||
|
printf 'Then point bVNC to 127.0.0.1:%s.\n' "$port"
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_files() {
|
||||||
|
local purge_password=${1:-0}
|
||||||
|
stop_service
|
||||||
|
disable_service
|
||||||
|
rm -f "$SERVICE_FILE"
|
||||||
|
rm -f "$RUNNER_FILE"
|
||||||
|
rm -f "$ENV_FILE"
|
||||||
|
if (( purge_password )); then
|
||||||
|
rm -f "$PASSWORD_FILE"
|
||||||
|
log "Removed password file."
|
||||||
|
fi
|
||||||
|
reload_user_daemon
|
||||||
|
log "Removed generated files."
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_non_root
|
||||||
|
|
||||||
|
local cmd="${1:-}"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
case "$cmd" in
|
||||||
|
setup)
|
||||||
|
local force=0
|
||||||
|
if [[ "${1:-}" == "--force-password" ]]; then
|
||||||
|
force=1
|
||||||
|
shift || true
|
||||||
|
fi
|
||||||
|
ensure_directories
|
||||||
|
install_dependencies
|
||||||
|
create_password_file "$force"
|
||||||
|
create_env_file
|
||||||
|
create_runner_script
|
||||||
|
create_service_file
|
||||||
|
reload_user_daemon
|
||||||
|
log "Setup complete. Start the service with: $SCRIPT_NAME start"
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
start_service
|
||||||
|
show_info
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop_service
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
stop_service
|
||||||
|
start_service
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status_service
|
||||||
|
;;
|
||||||
|
enable)
|
||||||
|
enable_service
|
||||||
|
;;
|
||||||
|
disable)
|
||||||
|
disable_service
|
||||||
|
;;
|
||||||
|
info)
|
||||||
|
show_info
|
||||||
|
;;
|
||||||
|
uninstall)
|
||||||
|
local purge=0
|
||||||
|
if [[ "${1:-}" == "--purge" ]]; then
|
||||||
|
purge=1
|
||||||
|
shift || true
|
||||||
|
fi
|
||||||
|
uninstall_files "$purge"
|
||||||
|
;;
|
||||||
|
help|--help|-h|"" )
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
die "Unknown command: $cmd"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
196
scripts/fix_virtualbox.sh
Normal file
196
scripts/fix_virtualbox.sh
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
on_error() {
|
||||||
|
local exit_code=$?
|
||||||
|
local line_number=$1
|
||||||
|
printf '\033[1;31m[ERROR]\033[0m Unexpected failure at line %s (exit code %s).\n' "${line_number}" "${exit_code}" >&2
|
||||||
|
}
|
||||||
|
trap 'on_error ${LINENO}' ERR
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
printf '\033[1;34m[INFO]\033[0m %s\n' "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_warn() {
|
||||||
|
printf '\033[1;33m[WARN]\033[0m %s\n' "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
printf '\033[1;31m[ERROR]\033[0m %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root() {
|
||||||
|
if [[ "${EUID}" -ne 0 ]]; then
|
||||||
|
log_error "This script must be run as root (try again with sudo)."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
require_pacman() {
|
||||||
|
if ! command -v pacman >/dev/null 2>&1; then
|
||||||
|
log_error "pacman not found. This script is intended for Arch Linux systems."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_kernel_release() {
|
||||||
|
uname -r
|
||||||
|
}
|
||||||
|
|
||||||
|
select_host_package() {
|
||||||
|
local kernel_release=$1
|
||||||
|
case "${kernel_release}" in
|
||||||
|
*-lts)
|
||||||
|
echo "virtualbox-host-modules-lts"
|
||||||
|
;;
|
||||||
|
*-arch*)
|
||||||
|
echo "virtualbox-host-modules-arch"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "virtualbox-host-dkms"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_kernel_headers() {
|
||||||
|
local -a headers=()
|
||||||
|
local kernel_pkg header_pkg
|
||||||
|
for kernel_pkg in linux linux-lts linux-zen linux-hardened; do
|
||||||
|
if pacman -Q "${kernel_pkg}" >/dev/null 2>&1; then
|
||||||
|
header_pkg="${kernel_pkg}-headers"
|
||||||
|
headers+=("${header_pkg}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ ${#headers[@]} -gt 0 ]]; then
|
||||||
|
printf '%s\n' "${headers[@]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
maybe_remove_conflicting_host_packages() {
|
||||||
|
local selected_package=$1
|
||||||
|
local -a candidates=("virtualbox-host-dkms" "virtualbox-host-modules-arch" "virtualbox-host-modules-lts")
|
||||||
|
local pkg
|
||||||
|
for pkg in "${candidates[@]}"; do
|
||||||
|
if [[ "${pkg}" != "${selected_package}" ]] && pacman -Q "${pkg}" >/dev/null 2>&1; then
|
||||||
|
log_warn "Removing conflicting package ${pkg} before installing ${selected_package}."
|
||||||
|
pacman -Rsn "${PACMAN_REMOVE_FLAGS[@]}" "${pkg}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
install_packages() {
|
||||||
|
local -a packages=()
|
||||||
|
local -a headers=()
|
||||||
|
local host_package=$1
|
||||||
|
shift
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
mapfile -t headers < <(printf '%s\n' "$@" | sort -u)
|
||||||
|
fi
|
||||||
|
packages+=("virtualbox" "virtualbox-guest-iso" "${host_package}")
|
||||||
|
if [[ "${host_package}" == "virtualbox-host-dkms" ]]; then
|
||||||
|
packages+=("dkms")
|
||||||
|
fi
|
||||||
|
if [[ ${#headers[@]} -gt 0 ]]; then
|
||||||
|
packages+=("${headers[@]}")
|
||||||
|
fi
|
||||||
|
log_info "Installing packages: ${packages[*]}"
|
||||||
|
pacman -S "${PACMAN_INSTALL_FLAGS[@]}" "${packages[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
rebuild_virtualbox_modules() {
|
||||||
|
local host_package=$1
|
||||||
|
if [[ "${host_package}" == "virtualbox-host-dkms" ]]; then
|
||||||
|
if command -v dkms >/dev/null 2>&1; then
|
||||||
|
log_info "Rebuilding VirtualBox DKMS modules for all installed kernels."
|
||||||
|
dkms autoinstall
|
||||||
|
else
|
||||||
|
log_warn "dkms command not found; skipping DKMS rebuild."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
reload_virtualbox_modules() {
|
||||||
|
log_info "Loading VirtualBox kernel modules."
|
||||||
|
if [[ -x /sbin/rcvboxdrv ]]; then
|
||||||
|
/sbin/rcvboxdrv setup || log_warn "rcvboxdrv reported an issue while setting up modules."
|
||||||
|
elif [[ -x /usr/lib/virtualbox/vboxdrv.sh ]]; then
|
||||||
|
/usr/lib/virtualbox/vboxdrv.sh setup || log_warn "vboxdrv.sh reported an issue while setting up modules."
|
||||||
|
fi
|
||||||
|
|
||||||
|
local -a modules=(vboxdrv vboxnetflt vboxnetadp vboxpci)
|
||||||
|
local mod
|
||||||
|
for mod in "${modules[@]}"; do
|
||||||
|
if ! lsmod | awk '{print $1}' | grep -Fxq "${mod}"; then
|
||||||
|
if ! modprobe "${mod}" >/dev/null 2>&1; then
|
||||||
|
log_warn "Module ${mod} failed to load; check dmesg for details."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! lsmod | awk '{print $1}' | grep -Fxq "vboxdrv"; then
|
||||||
|
log_error "VirtualBox kernel driver (vboxdrv) failed to load. Review /var/log and dmesg output for clues."
|
||||||
|
fi
|
||||||
|
log_info "VirtualBox kernel driver loaded successfully."
|
||||||
|
}
|
||||||
|
|
||||||
|
warn_if_secure_boot_enabled() {
|
||||||
|
local secure_boot_file
|
||||||
|
if [[ -d /sys/firmware/efi/efivars ]]; then
|
||||||
|
secure_boot_file=$(find /sys/firmware/efi/efivars -maxdepth 1 -name 'SecureBoot-*' -print -quit 2>/dev/null || true)
|
||||||
|
if [[ -n "${secure_boot_file}" && -r "${secure_boot_file}" ]]; then
|
||||||
|
local state
|
||||||
|
state=$(hexdump -n 1 -s 4 -e '1 "%d"' "${secure_boot_file}" 2>/dev/null || echo "0")
|
||||||
|
if [[ "${state}" == "1" ]]; then
|
||||||
|
log_warn "EFI Secure Boot appears to be enabled. You may need to sign VirtualBox modules manually."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remind_group_membership() {
|
||||||
|
local invoking_user=${SUDO_USER:-}
|
||||||
|
if [[ -n "${invoking_user}" && "${invoking_user}" != "root" ]]; then
|
||||||
|
if ! id -nG "${invoking_user}" | grep -qw "vboxusers"; then
|
||||||
|
log_warn "User ${invoking_user} is not in the vboxusers group. Add them with: sudo gpasswd -a ${invoking_user} vboxusers"
|
||||||
|
else
|
||||||
|
log_info "User ${invoking_user} is already in the vboxusers group."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
require_root
|
||||||
|
require_pacman
|
||||||
|
|
||||||
|
PACMAN_INSTALL_FLAGS=(--needed)
|
||||||
|
PACMAN_REMOVE_FLAGS=()
|
||||||
|
if [[ "${PACMAN_CONFIRM:-0}" == "1" ]]; then
|
||||||
|
log_info "PACMAN_CONFIRM=1 detected; pacman will prompt for confirmation."
|
||||||
|
else
|
||||||
|
PACMAN_INSTALL_FLAGS+=(--noconfirm)
|
||||||
|
PACMAN_REMOVE_FLAGS+=(--noconfirm)
|
||||||
|
fi
|
||||||
|
|
||||||
|
local kernel_release host_package
|
||||||
|
kernel_release=$(detect_kernel_release)
|
||||||
|
log_info "Detected running kernel: ${kernel_release}"
|
||||||
|
host_package=$(select_host_package "${kernel_release}")
|
||||||
|
log_info "Selected VirtualBox host package: ${host_package}"
|
||||||
|
|
||||||
|
mapfile -t kernel_headers < <(collect_kernel_headers)
|
||||||
|
if [[ "${host_package}" == "virtualbox-host-dkms" && ${#kernel_headers[@]} -eq 0 ]]; then
|
||||||
|
log_warn "No matching kernel headers detected. Ensure you've installed headers for your kernel so DKMS can build modules."
|
||||||
|
fi
|
||||||
|
|
||||||
|
maybe_remove_conflicting_host_packages "${host_package}"
|
||||||
|
install_packages "${host_package}" "${kernel_headers[@]}"
|
||||||
|
rebuild_virtualbox_modules "${host_package}"
|
||||||
|
reload_virtualbox_modules
|
||||||
|
warn_if_secure_boot_enabled
|
||||||
|
remind_group_membership
|
||||||
|
|
||||||
|
log_info "VirtualBox installation and driver setup complete."
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@ -120,7 +120,6 @@ function is_blocked_package_name() {
|
|||||||
|
|
||||||
# Explicitly blocked names list
|
# Explicitly blocked names list
|
||||||
local blocked=(
|
local blocked=(
|
||||||
"freetube-bin" "virtualbox" "virtualbox-host-modules-arch" "virtualbox-guest-iso" "virtualbox-ext-vnc" "virtualbox-guest-utils" "virtualbox-host-dkms"
|
|
||||||
"brave" "brave-bin" "freetube" "seamonkey-bin" "seamonkey" "min-browser-bin" "min-browser" "beaker-browser" "catalyst-browser-bin" "hamsket" "min"
|
"brave" "brave-bin" "freetube" "seamonkey-bin" "seamonkey" "min-browser-bin" "min-browser" "beaker-browser" "catalyst-browser-bin" "hamsket" "min"
|
||||||
"vieb-bin" "yt-dlp" "yt-dlp-git" "stremio" "stremio-git" "angelfish" "dooble" "eric" "falkon" "fiery" "maui" "konqueror" "liri" "otter"
|
"vieb-bin" "yt-dlp" "yt-dlp-git" "stremio" "stremio-git" "angelfish" "dooble" "eric" "falkon" "fiery" "maui" "konqueror" "liri" "otter"
|
||||||
"quotebrowser" "beaker" "catalyst" "badwolf" "eolie" "epiphany" "surf" "uzbl" "vimb" "vimb-git" "web-browser" "web-browser-git"
|
"quotebrowser" "beaker" "catalyst" "badwolf" "eolie" "epiphany" "surf" "uzbl" "vimb" "vimb-git" "web-browser" "web-browser-git"
|
||||||
@ -222,30 +221,6 @@ function check_for_steam() {
|
|||||||
return 1 # No steam package found
|
return 1 # No steam package found
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to check if user is trying to install virtualbox (always challenge-eligible package)
|
|
||||||
function check_for_virtualbox() {
|
|
||||||
# List of packages that require challenge (virtualbox packages)
|
|
||||||
local virtualbox_packages=("virtualbox" "virtualbox-host-modules-arch" "virtualbox-guest-iso" "virtualbox-ext-vnc" "virtualbox-guest-utils" "virtualbox-host-dkms")
|
|
||||||
|
|
||||||
# Check if the command is an installation command
|
|
||||||
if [[ "$1" == "-S" || "$1" == "-Sy" || "$1" == "-Syu" || "$1" == "-Syyu" || "$1" == "-U" ]]; then
|
|
||||||
# Check all arguments
|
|
||||||
for arg in "$@"; do
|
|
||||||
# Strip repository prefix if present (like extra/ or community/)
|
|
||||||
local package_name="${arg##*/}"
|
|
||||||
|
|
||||||
# Check if argument matches virtualbox
|
|
||||||
for package in "${virtualbox_packages[@]}"; do
|
|
||||||
if [[ "$arg" == "$package" || "$arg" == *"/$package-"* || "$arg" == *"/$package/"* ||
|
|
||||||
"$arg" == *"/$package" || "$package_name" == "$package" ]]; then
|
|
||||||
return 0 # VirtualBox package found
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
return 1 # No virtualbox package found
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to check if current day is a weekday (after 4PM Friday until midnight Sunday)
|
# Function to check if current day is a weekday (after 4PM Friday until midnight Sunday)
|
||||||
function is_weekday() {
|
function is_weekday() {
|
||||||
local day_of_week=$(date +%u) # %u gives 1-7 (Monday is 1, Sunday is 7)
|
local day_of_week=$(date +%u) # %u gives 1-7 (Monday is 1, Sunday is 7)
|
||||||
@ -535,14 +510,6 @@ if check_for_always_blocked "$@"; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check for virtualbox (always challenge-eligible package)
|
|
||||||
if check_for_virtualbox "$@"; then
|
|
||||||
prompt_for_virtualbox_challenge
|
|
||||||
if [[ $? -ne 0 ]]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check for steam (challenge-eligible package)
|
# Check for steam (challenge-eligible package)
|
||||||
if check_for_steam "$@"; then
|
if check_for_steam "$@"; then
|
||||||
prompt_for_steam_challenge
|
prompt_for_steam_challenge
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user