df3e4b15db
新增 macOS 快捷指令自动化工具,包含虚拟机权限修复脚本(vm-permission-fixer.sh)及远程引导启动器(vm-permission-fixer-bootstrap.sh)。 - 在打开 Parallels Desktop 时,自动修复指定 .pvm 虚拟机包的所有权。 - 采用 macOS Keychain 安全存储和读取 sudo 密码,避免明文硬编码。 - 补充 shortcuts 模块相关的中英文说明文档,并同步更新项目主 README。
126 lines
4.0 KiB
Bash
Executable File
126 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# 脚本名称: VM Permission Fixer
|
|
# 日志路径: ~/Library/Logs/vm_permission_fix.log
|
|
|
|
set -u
|
|
set -o pipefail
|
|
|
|
PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin"
|
|
export PATH
|
|
|
|
CURRENT_USER="$(id -un)"
|
|
VM_PATH="${VM_FIXER_VM_PATH:-/Users/Shared/Parallels/VM/Windows 11/Windows 11.pvm}"
|
|
LOG_FILE="${VM_FIXER_LOG_FILE:-/Users/${CURRENT_USER}/Library/Logs/vm_permission_fix.log}"
|
|
KEYCHAIN_SERVICE="${VM_FIXER_KEYCHAIN_SERVICE:-vm-permission-fixer-sudo-password}"
|
|
ASKPASS_TEMP="$(mktemp "${TMPDIR:-/tmp}/vm-permission-fixer-askpass.XXXXXX.sh")"
|
|
|
|
cleanup() {
|
|
rm -f "$ASKPASS_TEMP"
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
exec 1>>"$LOG_FILE"
|
|
exec 2>&1
|
|
|
|
log() {
|
|
local level="$1"
|
|
local message="$2"
|
|
local timestamp
|
|
timestamp="$(date "+%Y-%m-%d %H:%M:%S")"
|
|
printf "[%s] [%-7s] %s\n" "$timestamp" "$level" "$message"
|
|
}
|
|
|
|
notify() {
|
|
local title="$1"
|
|
local message="$2"
|
|
/usr/bin/osascript -e "display notification \"${message}\" with title \"${title}\"" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
notify_error_and_exit() {
|
|
local message="$1"
|
|
local code="${2:-1}"
|
|
log "ERROR" "$message"
|
|
notify "自动化错误" "$message"
|
|
exit "$code"
|
|
}
|
|
|
|
print_separator() {
|
|
echo "--------------------------------"
|
|
}
|
|
|
|
print_banner() {
|
|
echo ""
|
|
echo "==================================="
|
|
log "INIT" "自动化任务启动: 修复 Parallels Desktop 虚拟机权限"
|
|
echo "==================================="
|
|
}
|
|
|
|
prompt_and_save_sudo_password() {
|
|
local password
|
|
log "INFO" "Keychain 未找到 sudo 密码,准备首次保存。"
|
|
|
|
password="$(
|
|
/usr/bin/osascript <<'APPLESCRIPT'
|
|
set dialogResult to display dialog "首次使用请输入一次 macOS 登录密码 / sudo 密码,脚本会保存到 Keychain,后续自动读取。" default answer "" with hidden answer buttons {"取消", "保存"} default button "保存" with title "VM Permission Fixer"
|
|
text returned of dialogResult
|
|
APPLESCRIPT
|
|
)" || return 1
|
|
|
|
if [[ -z "$password" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
/usr/bin/security add-generic-password -U -a "$CURRENT_USER" -s "$KEYCHAIN_SERVICE" -w "$password" >/dev/null
|
|
unset password
|
|
}
|
|
|
|
setup_sudo_askpass() {
|
|
cat > "$ASKPASS_TEMP" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
exec /usr/bin/security find-generic-password -a "${USER:-$(id -un)}" -s "${VM_FIXER_KEYCHAIN_SERVICE:-vm-permission-fixer-sudo-password}" -w
|
|
EOF
|
|
chmod 700 "$ASKPASS_TEMP"
|
|
export SUDO_ASKPASS="$ASKPASS_TEMP"
|
|
export VM_FIXER_KEYCHAIN_SERVICE="$KEYCHAIN_SERVICE"
|
|
|
|
if ! /usr/bin/security find-generic-password -a "$CURRENT_USER" -s "$KEYCHAIN_SERVICE" -w >/dev/null 2>&1; then
|
|
prompt_and_save_sudo_password || notify_error_and_exit "未保存 sudo 密码,脚本终止。"
|
|
fi
|
|
|
|
log "ACTION" "正在通过 Keychain 准备 sudo 凭据..."
|
|
if ! /usr/bin/sudo -A -v; then
|
|
log "ERROR" "Keychain 中的 sudo 密码不可用。"
|
|
log "DEBUG" "可执行以下命令删除后重试: security delete-generic-password -a \"$CURRENT_USER\" -s \"$KEYCHAIN_SERVICE\""
|
|
notify "自动化错误" "Keychain 中的 sudo 密码不可用,请查看日志"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
print_banner
|
|
|
|
log "INFO" "当前用户: $CURRENT_USER"
|
|
log "INFO" "目标路径: $VM_PATH"
|
|
|
|
if [[ ! -e "$VM_PATH" ]]; then
|
|
print_separator
|
|
notify_error_and_exit "找不到虚拟机文件,请检查日志"
|
|
fi
|
|
|
|
setup_sudo_askpass
|
|
|
|
log "ACTION" "正在尝试更改文件所有权 (chown -R)..."
|
|
if /usr/bin/sudo -A /usr/sbin/chown -R "$CURRENT_USER" "$VM_PATH"; then
|
|
log "SUCCESS" "权限修复成功。"
|
|
log "DONE" "自动化任务完成 (Exit Code: 0)"
|
|
print_separator
|
|
notify "自动化完成" "虚拟机权限已修复"
|
|
else
|
|
exit_code=$?
|
|
log "ERROR" "操作失败,Exit Code: $exit_code"
|
|
log "DEBUG" "可能原因: 密码错误 / SIP 限制 / 文件被占用 / Keychain 被锁定"
|
|
print_separator
|
|
notify "自动化错误" "权限修复失败,请查看日志"
|
|
exit "$exit_code"
|
|
fi
|