Last active
February 14, 2022 04:26
-
-
Save nhed/bee12a0f80e0ed4fe1b22482d7968cfe to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/bash | |
# | |
# NYH | |
# Created as wrapper for PIA's installer, then feed to FPM | |
# | |
# Modifies installer's hardcoded PATH to start with directory with our fake | |
# bins like `sudo` & `getent` | |
# | |
# progname: as invoked, base-name of this file or symlink | |
declare progname="${0##*/}" | |
# full path to this file w/o symlinks | |
declare realprogpath=$(realpath "${0}") | |
function die { | |
echo "${*}" >&2 | |
exit 1 | |
} | |
function usage { | |
cat<<_EOF_ >&2 | |
${progname} piavpn | |
PreReqs: | |
- 'fpm' (Effing Package Management, https://github.com/jordansissel/fpm/) | |
Installed with "gem install fpm". | |
- 'rpmbuild', installed with "sudo dnf install rpm-build" | |
_EOF_ | |
} | |
# usually just prepend with "${INSTALL_PREFIX}", but have exceptions | |
function xlate_path { | |
local ret="${INSTALL_PREFIX}${1}" | |
case "${1}" in | |
/etc/iproute2/rt_tables) | |
ret="${INSTALL_PREFIX}/etc/iproute2/rt_tables.d/${SHORT_APP_ID}.conf" | |
mkdir -p "${ret%/*}" | |
;; | |
esac | |
echo "${ret}" | |
} | |
# Arg: copy target, could be dir or file | |
function maybe_mkdir { | |
local tgt="${1}"; shift | |
local tgtdir="${tgt%/*}" | |
# we cant tell if tgt is supposed to be a file or a dir if the | |
# installed create it already with a mkdir, install etc - nothing for | |
# us to do here | |
[ -e "$(xlate_path "${tgt}")" ] && return 0 | |
[ -e "$(xlate_path "${tgtdir}")" ] && return 0 | |
# if however the installer assumed or tested target dir exists we | |
# should repeat their test and create a dir in install cp allows copy | |
# dest to be a dir name or a file name if the last are is not on the | |
# target as a directory | |
if [ -d "${tgt}" ]; then | |
/usr/bin/fakeroot mkdir -p "$(xlate_path "${tgt}")" | |
elif [ -d "${tgtdir}" ]; then | |
/usr/bin/fakeroot mkdir -p "$(xlate_path "${tgtdir}")" | |
else | |
# do the same thing but whine about it becaus eits probably wrong | |
echo "Making ${tgtdir} under ${INSTALL_PREFIX}," \ | |
"should not have to do that" >&2 | |
/usr/bin/fakeroot mkdir -p "$(xlate_path "${tgtdir}")" | |
fi | |
} | |
# sudo cmd + args | |
# Augents commands so they can operate relative to INSTALL_PREFIX | |
# (where original installer had no notion of installing into a | |
# different location). | |
# invoked with INSTALL_PREFIX env var | |
function instfix_sudo { | |
[[ -z "${1}" ]] && die "Must have at least one arg, the command" | |
[[ "${1}" =~ ^- ]] && die "Not supporting args to sudo beyond the command" | |
declare -a args=() | |
[[ -z "${INSTALL_PREFIX}" ]] && die "Need the INSTALL_PREFIX env var" | |
mkdir -p "${INSTALL_PREFIX}" | |
# just primary command checks | |
# knew to look for them with | |
# grep sudo .../install.sh| sed 's@^.*sudo \([^ ]*\).*$@\1@'|sort -u | |
case "${1}" in | |
# packagae managers we dont (yet?) handle | |
pacman|zypper|apt-get) | |
die "Cant do that: \"${*}\", maybe declare dependencies" | |
;; | |
# packagae managers we can handle | |
# assuming only "install" subcommand | |
dnf|yum) | |
shift | |
while [ ${#} -gt 0 ]; do | |
if [[ "${1}" != "-y" ]] && [[ "${1}" != "install" ]]; then | |
echo "${1}" >> "${INSTALL_DEPS}" | |
fi | |
shift | |
done | |
exit 0 | |
;; | |
# TODO: check of the setcap perist, doubt it, in unpacked files or | |
# need postinstall/or? | |
update-desktop-database|systemctl|service|rc-update|rc-service|update-rc.d) | |
echo "${@}" >> "${INSTALL_SCRIPTS}/postinstall" | |
exit 0 | |
;; | |
groupadd) | |
echo "getent group ${@:(-1)} >/dev/null || ${*} || true" >> \ | |
"${INSTALL_SCRIPTS}/preinstall" | |
exit 0 | |
;; | |
useradd) | |
echo "getent passwd ${@:(-1)} >/dev/null || ${*} || true" >> \ | |
"${INSTALL_SCRIPTS}/preinstall" | |
exit 0 | |
;; | |
# make sure that if directory exists in "real" life make it also | |
# in INSTALL_PREFIX. | |
cp|/bin/cp|tee) | |
args+=("${1}") | |
maybe_mkdir "${@:(-1)}" | |
shift | |
;; | |
# we do not want the target to point to an absolute path starting | |
# with INSTALL_PREFIX. While there are several possible solution | |
# the easiest is probably to make them into relative links, adding | |
# `-r` (yes, assuming a symlink). | |
ln) | |
args+=("${1}" "-r") | |
maybe_mkdir "${@:(-1)}" | |
shift | |
;; | |
# these are "blessed" let pass as is | |
chmod|touch|mkdir|rm) | |
args+=("${1}") | |
shift | |
;; | |
*) | |
die "Hmmm, this wasnt on the test: ${*}" | |
;; | |
esac | |
# rest of args | |
while [ ${#} -gt 0 ]; do | |
case "${1}" in | |
# not sure we need this *shrug* | |
/dev/null) | |
args+=("${1}") | |
;; | |
# We unfortunatley need to use the eval here because the caller, | |
# not aware of INSTALL_PREFIX is unable to expand wildcards when | |
# files were copied into the INSTALL_PREFIX | |
/opt/*|/usr/*|/etc/*) | |
args+=($(eval echo $(xlate_path "${1}"))) | |
;; | |
*) | |
args+=("${1}") | |
;; | |
esac | |
shift | |
done | |
exec /usr/bin/fakeroot "${args[@]}" | |
} | |
# fake getent it so it always looks like reuqired users and groups are not | |
# installed yet. | |
function instfix_getent { | |
case "${1}" in | |
group|passwd) | |
return 1 | |
;; | |
*) | |
exec /usr/bin/getent "${@}" | |
;; | |
esac | |
} | |
# fake ldconfig to always return an empty list, triggering the installer | |
# to attempt and demand all the deps - which we will collate regardkess of | |
# what is currently installed. | |
function instfix_ldconfig { | |
return 0 | |
} | |
# locinst <tmpdir> <orig-installer> <short-id> [installer-args] | |
# Caller creates <tmpdir>. After succesful run of modified installer | |
# <tmpdir>/dest will be the root of all the files to be installed and | |
# <tmpdir>/scripts will have various scripts | |
function instfix_locinst { | |
local tmpdir="${1}"; shift | |
local orig_inst="${1}"; shift | |
export SHORT_APP_ID="${1}"; shift | |
[[ -z "${tmpdir}" ]] && \ | |
die "Missing temp directory path." | |
[[ -d "${tmpdir}" ]] || \ | |
die "Temp dir \"${tmpdir}\" does not exist (or not dir)." | |
[[ -z "${orig_inst}" ]] && \ | |
die "Missing installed script." | |
[[ -e "${orig_inst}" ]] || \ | |
die "Specified installer script (${orig_inst}) does not exist." | |
[[ "$(file -b --mime-type "${orig_inst}")" == "text/x-shellscript" ]] || \ | |
die "Expected a shell script, ${orig_inst}" | |
# use absolute path as script could cd arond | |
tmpdir="$(realpath "${tmpdir}")" | |
export INSTALL_PREFIX="${tmpdir}/dest" | |
mkdir -p "${INSTALL_PREFIX}" | |
export INSTALL_SCRIPTS="${tmpdir}/scripts" | |
mkdir -p "${INSTALL_SCRIPTS}" | |
export INSTALL_DEPS="${tmpdir}/deps" | |
local fakebins="${tmpdir}/fake" | |
mkdir -p "${fakebins}" | |
for bin in "sudo" "getent" "ldconfig"; do | |
ln -s "${realprogpath}" "${fakebins}/${bin}" | |
done | |
# Just in case installed calls sudo in a way that bypases our search | |
# path we don't want cached passwods to allow it to proceed without | |
# prompting. | |
/usr/bin/sudo -k | |
# Prepend the fakebins dir in frot of PATH in both | |
# - installer script itself if it overrides it. | |
# - in the callling environment in case it does not override. | |
sed 's@\(PATH="\)@\1'"${fakebins}:@" "${orig_inst}" > \ | |
"${orig_inst}-mod" | |
chmod +x "${orig_inst}-mod" | |
PATH="${fakebins}:${PATH}" "${orig_inst}-mod" "${@}" | |
for unitfile in \ | |
"${INSTALL_PREFIX}/etc/systemd/system/"*.{service,timer,socket} \ | |
"${INSTALL_PREFIX}/usr/lib/systemd/system/"*.{service,timer,socket} | |
do | |
[ -e "${unitfile}" ] || continue | |
echo "systemctl stop ${unitfile##*/}" >> \ | |
"${INSTALL_SCRIPTS}/preremove" | |
echo "systemctl disable ${unitfile##*/}" >> \ | |
"${INSTALL_SCRIPTS}/preremove" | |
done | |
for script in "${INSTALL_SCRIPTS}/"*; do | |
[ -e "${script}" ] || continue | |
{ echo '#!/usr/bin/bash'; cat "${script}"; } > "${script}.new" | |
mv "${script}.new" "${script}" | |
chmod +x "${script}" | |
done | |
} | |
# package <tmpdir> <short-id> <src-url> | |
# where <tmpdir> is expected to have the following: | |
# - `dest`: A directory that is the base of the package contents. | |
# - `scripts`: A directory with pre/post install etc scripts. | |
# - `deps`: A file listing dependencies, one dependency per line. | |
# <short-id> is a short name for the package | |
# <src-url>: used for package metadata | |
function instfix_package { | |
local dir="${1}"; shift | |
local short_id="${1}"; shift | |
local src_url="${1}"; shift | |
local -a fpmcmd=("fpm" "-s" "dir" "-t" "rpm") | |
local version | |
case "${short_id}" in | |
piavpn) | |
version=$(head -1 "${dir}/dest/opt/piavpn/share/version.txt") | |
#fpmcmd+=("--config-files" "/opt/piavpn/etc/") | |
fpmcmd+=("-x" "/opt/piavpn/bin/pia-uninstall.sh") | |
fpmcmd+=("-x" "/opt/piavpn/bin/install-wireguard.sh") | |
fpmcmd+=("--url" "https://www.privateinternetaccess.com/download/linux-vpn") | |
fpmcmd+=( | |
"--description" | |
"Private Internet Access (PIA) VPN | |
Repackaged into an RPM from ${src_url} | |
$(cat ${dir}/dest/opt/piavpn/share/version.txt)" | |
) | |
;; | |
*) | |
version="0.0.1" | |
;; | |
esac | |
fpmcmd+=("-n" "${short_id}") | |
fpmcmd+=("-v" "${version}") | |
fpmcmd+=("--directories" "/opt/piavpn") | |
fpmcmd+=("-C" "${dir}/dest") | |
if [ -f "${dir}/deps" ]; then | |
while read -r dep ; do | |
fpmcmd+=("-d" "${dep}") | |
done < "${dir}/deps" | |
fi | |
for script in "${dir}/scripts/"*; do | |
[[ -e "${script}" ]] || continue | |
case "${script}" in | |
*/postinstall) | |
fpmcmd+=("--after-install" "${script}") | |
;; | |
*/preinstall) | |
fpmcmd+=("--before-install" "${script}") | |
;; | |
*/preremove) | |
fpmcmd+=("--before-remove" "${script}") | |
;; | |
*) | |
die "Oops did not handle script \"${script}\"" | |
;; | |
esac | |
done | |
mkdir -p "${dir}/fpmwork/" | |
fpmcmd+=("--workdir" "${dir}/fpmwork/") | |
echo "Running FPM" >&2 | |
"${fpmcmd[@]}" | |
} | |
function instfix_piavpn { | |
local tmpdir="$(mktemp -d)" | |
trap "rm -rf ${tmpdir}" EXIT | |
# download & expand | |
local script_url=$( | |
curl -sG https://www.privateinternetaccess.com/download/linux-vpn | \ | |
grep download_linux | \ | |
sed 's@^.* href="\([^"]*\)".*$@\1@' | sort -u | \ | |
tail -1 | |
) | |
mkdir "${tmpdir}/src" | |
curl -sGL "${script_url}" > "${tmpdir}/piavpn.run" | |
chmod +x "${tmpdir}/piavpn.run" | |
"${tmpdir}/piavpn.run" --noexec --target "${tmpdir}/src" | |
# locinstall & package | |
instfix_locinst "${tmpdir}" "${tmpdir}/src/install.sh" piavpn --systemd | |
instfix_package "${tmpdir}" piavpn "${script_url}" | |
} | |
# MAIN | |
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then | |
declare func | |
if [[ -h "${0}" ]] && type -t "instfix_${progname}" > /dev/null; then | |
func="instfix_${progname}" | |
elif [ "${1}" ] && type -t "instfix_${1}" > /dev/null; then | |
func="instfix_${1}" | |
shift | |
elif [ -z "${1}" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then | |
usage | |
exit 1 | |
else | |
echo "Invalid subcommand: \"${1}\"" >&2 | |
usage | |
exit 2 | |
fi | |
"${func}" "${@}" | |
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PIA's linux installer is not utilizing packages. Supposed to be run as non root with sudo prompts. | |
There might be a better way to do this. | |
THis script mocks few executables while running PIAs installer and then feeds data to FPM to build an RPM. | |
Probably could use more error checking and code cleanup. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment