BackgroundMusic/BGMApp/BGMXPCHelper/post_install.sh

190 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/>.
#
# post_install.sh
# BGMXPCHelper
#
# Copyright © 2016, 2017 Kyle Neideck
#
# Installs BGMXPCHelper's launchd plist file and "bootstraps" (registers/enables) it with launchd.
#
# When you install BGMXPCHelper with xcodebuild (or Xcode itself, if that's possible) this script
# runs as the final build phase.
#
PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
# Check we have the paths we need, either in environment variables from Xcode or from the args.
if [[ -z $1 ]]; then
if [[ -z ${INSTALL_DIR} ]]; then
echo "Environment variable INSTALL_DIR was not set." >&2
exit 1
fi
else
INSTALL_DIR="$1"
fi
if [[ -z $2 ]]; then
if [[ -z ${EXECUTABLE_PATH} ]]; then
echo "Environment variable EXECUTABLE_PATH was not set." >&2
exit 1
fi
else
EXECUTABLE_PATH="$2"
fi
if [[ -z $3 ]]; then
if [[ -z ${TARGET_BUILD_DIR} ]]; then
echo "Environment variable TARGET_BUILD_DIR was not set." >&2
exit 1
fi
if [[ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH} ]]; then
echo "Environment variable UNLOCALIZED_RESOURCES_FOLDER_PATH was not set." >&2
exit 1
fi
RESOURCES_PATH="${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
else
RESOURCES_PATH="$3"
fi
# Safe mode.
set -euo pipefail
IFS=$'\n\t'
# Show a warning if INSTALL_DIR isn't set to a safe installation directory.
if [[ $(bash "${RESOURCES_PATH}/safe_install_dir.sh" "${INSTALL_DIR}") != 1 ]]; then
echo "$(tput setaf 11)WARNING$(tput sgr0): Installing to \"${INSTALL_DIR}\" may be" \
"insecure. See safe_install_dir.sh for details." >&2
fi
HELPER_USER=_BGMXPCHelper
LAUNCHD_PLIST_INSTALL_PATH=/Library/LaunchDaemons
LAUNCHD_PLIST_FILENAME=com.bearisdriving.BGM.XPCHelper.plist
LAUNCHD_PLIST="${LAUNCHD_PLIST_INSTALL_PATH}/${LAUNCHD_PLIST_FILENAME}"
# Create an unprivileged user for BGMXPCHelper to run as.
if [[ "$(dscl . -search /Users RecordName ${HELPER_USER})" == "" ]]; then
# Find an unused UID and GID. UIDs from 0 to 500 are reserved by OSX.
HELPER_UID=$(dscl . -list /Users UniqueID | \
awk '{ a[$2] } END { for (i=501; i in a; i++); print i }')
HELPER_GID=$(dscl . -list /Groups PrimaryGroupID | \
awk '{ a[$2] } END { for (i='${HELPER_UID}'; i in a; i++); print i }')
# Check the UID and GID.
NUMERIC_ID_REGEX='^[1-9][0-9]*$'
([[ "${HELPER_UID}" =~ ${NUMERIC_ID_REGEX} ]] && \
[[ ${HELPER_UID} -gt 500 ]] && \
[[ "$(dscl . -search /Users UniqueID ${HELPER_UID})" == "" ]]) || \
(echo "Internal error. Failed to generate a user ID. HELPER_UID=${HELPER_UID}" >&2; exit 1)
([[ "${HELPER_GID}" =~ ${NUMERIC_ID_REGEX} ]] && \
[[ ${HELPER_GID} -gt 500 ]] && \
[[ "$(dscl . -search /Groups PrimaryGroupID ${HELPER_GID})" == "" ]]) || \
(echo "Internal error. Failed to generate a group ID. HELPER_GID=${HELPER_GID}" >&2; exit 1)
# Create the group.
sudo dscl . -create /Groups/${HELPER_USER} PrimaryGroupID ${HELPER_GID}
sudo dscl . -create /Groups/${HELPER_USER} RealName "Background Music XPC Helper Group"
sudo dscl . -create /Groups/${HELPER_USER} Password '*'
# Create the user.
sudo dscl . -create /Users/${HELPER_USER} UniqueID ${HELPER_UID}
sudo dscl . -create /Users/${HELPER_USER} PrimaryGroupID ${HELPER_GID}
sudo dscl . -create /Users/${HELPER_USER} RealName "Background Music XPC Helper"
sudo dscl . -create /Users/${HELPER_USER} Password '*'
sudo dscl . -create /Users/${HELPER_USER} UserShell /usr/bin/false
sudo dscl . -create /Users/${HELPER_USER} NFSHomeDirectory /var/empty
sudo dscl . -delete /Users/${HELPER_USER} AuthenticationAuthority
# Add the user to the group
sudo dscl . -append /Groups/${HELPER_USER} GroupMembership ${HELPER_USER}
# Check the user and group were created.
[[ "$(dscl . -search /Users RecordName ${HELPER_USER})" != "" ]] || \
(echo "Internal error. Failed to ${HELPER_USER} user." >&2; exit 1)
[[ "$(dscl . -search /Groups RecordName ${HELPER_USER})" != "" ]] || \
(echo "Internal error. Failed to ${HELPER_USER} group." >&2; exit 1)
echo "Created ${HELPER_USER} user."
fi
# Copy the plist template into place.
sudo cp "${RESOURCES_PATH}/${LAUNCHD_PLIST_FILENAME}.template" "${LAUNCHD_PLIST}"
# Set the plist's owner and permissions. (Probably not necessary, but just in case.)
sudo chown root:wheel "${LAUNCHD_PLIST}"
sudo chmod 0644 "${LAUNCHD_PLIST}"
# Replace the template variables in the plist.
# First, escape the / characters in the values because we're using / in our sed pattern. The
# solution usually recommended is to use | or # instead of /, but file and directory names can
# contain those characters so I think this solution is safer.
#
# This is using Bash substring replacement to replace every occurrence of "/" with "\/". The general
# form is ${string//substring/replacement}. (${string/substring/replacement} only replaces the first
# match.)
INSTALL_DIR_ESCAPED="${INSTALL_DIR//\//\\/}"
EXECUTABLE_PATH_ESCAPED="${EXECUTABLE_PATH//\//\\/}"
# Replace the variables in-place. They're formatted like "{{TEMPLATE_VARIABLE}}".
sudo sed -i.tmp "s/{{PATH_TO_BGMXPCHELPER}}/${INSTALL_DIR_ESCAPED}/g" "${LAUNCHD_PLIST}"
# EXECUTABLE_PATH is set by Xcode, currently to "BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper".
sudo sed -i.tmp "s/{{BGMXPCHELPER_EXECUTABLE_PATH}}/${EXECUTABLE_PATH_ESCAPED}/g" "${LAUNCHD_PLIST}"
sudo sed -i.tmp "s/{{BGMXPCHELPER_USER_NAME}}/${HELPER_USER}/g" "${LAUNCHD_PLIST}"
sudo sed -i.tmp "s/{{BGMXPCHELPER_GROUP_NAME}}/${HELPER_USER}/g" "${LAUNCHD_PLIST}"
# Remove template-only comments.
sudo sed -i.tmp 's/{{#.*#}}//g' "${LAUNCHD_PLIST}"
# Clean up sed's temporary file.
sudo rm -f "${LAUNCHD_PLIST}.tmp"
echo "Installed ${LAUNCHD_PLIST_FILENAME} to ${LAUNCHD_PLIST_INSTALL_PATH}."
# Unregister the plist. This disables the existing version of BGMXPCHelper.
echo "Unregistering ${LAUNCHD_PLIST}. This will print errors if you don't already have a version" \
"of it registered, or your system uses an older version of launchctl. They're safe to ignore."
echo "----"
# The fallback versions of this command are for OS X 10.10 and 10.9, respectively. The "|| true"
# part is so the command can fail, which it does if the plist isn't already installed, without -e
# killing the script.
sudo launchctl bootout system "${LAUNCHD_PLIST}" || \
sudo launchctl unbootstrap system "${LAUNCHD_PLIST}" || \
sudo launchctl unload "${LAUNCHD_PLIST}" || \
true
echo "----"
# Register the plist with launchd. This enables BGMXPCHelper.
# The fallback version of this command is for OS X 10.9.
sudo launchctl bootstrap system "${LAUNCHD_PLIST}" || \
sudo launchctl load "${LAUNCHD_PLIST}"
echo "Started the BGMXPCHelper service."