Files
Kyle Neideck b96fcb7a47 Fix BGMXPCHelper not launching due to Gatekeeper in macOS 14.5.
The previous fix was removing the quarantine attribute from the
BGMXPCHelper.xpc dir, but in 14.5 its contents also get the attribute.
(I haven't tested in earlier versions.) This new fix removes the
attribute from the contents as well.
2024-04-26 09:50:08 +10:00

181 lines
7.4 KiB
Bash

#!/bin/bash
# vim: tw=100:
# This file is part of Background Music.
#
# Background Music is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 2 of the
# License, or (at your option) any later version.
#
# Background Music is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Background Music. If not, see <http://www.gnu.org/licenses/>.
#
# postinstall
#
# Copyright © 2017-2022, 2024 Kyle Neideck
# Copyright © 2023 modue sp. z o.o.
#
# Make sure we use the built-in versions of programs, for consistency. Probably not necessary
# because the installer will set $PATH in a similar way, but it doesn't hurt.
PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
function log {
logger "$@"
echo "$@"
}
function log_output {
while read line
do
log "$line"
done < /dev/stdin
}
coreaudiod_plist="/System/Library/LaunchDaemons/com.apple.audio.coreaudiod.plist"
dest_volume="$3"
xpc_helper_install_path="$(bash safe_install_dir.sh -y)"
log "Installing BGMXPCHelper to $xpc_helper_install_path"
# Delete the old version first, if any, to work around a macOS bug where a codesigned Mach-O binary
# can fail verification if you overwrite the old version instead of replacing it. Apparently the
# kernel caches some codesigning info that doesn't get updated. See
# <https://forums.developer.apple.com/thread/126187>.
rm -rf "${xpc_helper_install_path}/BGMXPCHelper.xpc"
cp -Rf "BGMXPCHelper.xpc" "$xpc_helper_install_path"
# Remove the quarantine attribute from BGMXPCHelper. Since macOS 14.0, this is needed to keep
# Gatekeeper from trying to show a permission prompt when BGMXPCHelper is launched. That would
# fail because BGMXPCHelper is a daemon, and Gatekeeper would prevent it from launching.
#
# TODO: I think, ideally, we should use the new Service Management API to install BGMXPCHelper,
# which would probably avoid this issue. See
# <https://developer.apple.com/documentation/servicemanagement/updating_your_app_package_installer_to_use_the_new_service_management_api>.
log "Removing com.apple.quarantine attribute from BGMXPCHelper."
sudo xattr -dr com.apple.quarantine "${xpc_helper_install_path}/BGMXPCHelper.xpc" 2>&1 | log_output
ls -lah@ "${xpc_helper_install_path}/BGMXPCHelper.xpc" 2>&1 | log_output
# TODO: Fail the install and show an error message if this fails.
bash "post_install.sh" "$xpc_helper_install_path" "BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper" "."
# TODO: Verify the installed files, their permissions, the _BGMXPCHelper user/group, etc.
# The extra or-clauses are fallback versions of the command that restarts coreaudiod. Apparently
# some of these commands don't work with older versions of launchctl, so I figure there's no
# harm in trying a bunch of different ways (which should all work).
(sudo launchctl kickstart -k system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \
sudo killall coreaudiod &>/dev/null || \
(sudo launchctl unload "$coreaudiod_plist" &>/dev/null && \
sudo launchctl load "$coreaudiod_plist" &>/dev/null)
) && \
sleep 2
# Wait until coreaudiod has restarted and BGMDevice is ready to use.
retries=5
while [[ $retries -gt 0 ]]; do
device_list="$(system_profiler SPAudioDataType)"
# The system_profiler command above doesn't work on GitHub Actions, so also try this
# alternative, which does. I haven't looked into the root cause, so this may be necessary for
# other systems as well.
input_device_list="$(./ListInputDevices)"
# "BGMDevice" is the UID, which is a stable identifier. device_list doesn't include the UIDs, so
# we have to check for BGMDevice's name.
if [[ "$device_list" =~ "Background Music" ]] || \
[[ "$input_device_list" =~ "BGMDevice" ]]; then
log "Background Music device is installed and available."
# Break out of the loop.
retries=0
else
retries=$((retries - 1))
if [[ $retries -gt 0 ]]; then
log "Background Music device not found. Trying again in 3 seconds..."
sleep 3
else
# TODO: We might be able to use <installation-check> to show error messages in the
# installer GUI instead of just logging them. See
# <https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW12>.
log "Background Music device not found. Installation failed."
log "Audio devices:"
log "$device_list"
log "Audio input devices:"
log "$input_device_list"
exit 1
fi
fi
done
# Launch BGMApp.
#
# Allow a few retries because it seems to sometimes fail intermittently in CI builds. For example,
# <https://travis-ci.org/github/kyleneideck/BackgroundMusic/jobs/764126689>. From the error
# messages, it looks like BGMApp isn't always registered with Launch Services by this point.
logged_in_user_id="$(id -u "${USER}")"
did_open_bgmapp=false
retries=5
while [[ $did_open_bgmapp != "true" ]] && [[ $retries -gt 0 ]]; do
retries=$((retries - 1))
# Try opening BGMApp using its bundle ID first so the installer can declare it as "relocatable".
# That way, if the user moves BGMApp and then installs a newer version of Background Music, the
# installer will try to find the old version of BGMApp and put the new one in the same place.
#
# Use launchctl to make sure we don't run BGMApp as the installer user.
#
# TODO: If they have multiple copies of BGMApp, this might open one of the old ones.
log "Opening Background Music.app by bundle ID"
if launchctl asuser "${logged_in_user_id}" \
open -b com.bearisdriving.BGM.App; then
did_open_bgmapp=true
fi
if [[ $did_open_bgmapp != "true" ]]; then
dest_volume_no_trailing_slash="${dest_volume/%\//}"
log "Opening ${dest_volume_no_trailing_slash}/Applications/Background Music.app"
if launchctl asuser "${logged_in_user_id}" \
open "${dest_volume_no_trailing_slash}/Applications/Background Music.app"; then
did_open_bgmapp=true
fi
fi
if [[ $did_open_bgmapp != "true" ]]; then
log "Opening Background Music.app using AppleScript"
if osascript -e 'tell application "Background Music" to activate'; then
did_open_bgmapp=true
fi
fi
if [[ $did_open_bgmapp != "true" ]]; then
log "Failed to open Background Music.app. Will retry shortly."
sleep 1
fi
done
# If we couldn't open BGMApp, it probably didn't install properly, but it's not certain, so don't
# fail the install.
if [[ $did_open_bgmapp != "true" ]]; then
log "Failed to open Background Music.app. Giving up."
fi
# The installer plays a sound when it finishes, so give BGMApp a second to launch.
sleep 1
exit 0