scripts: imp install

This commit is contained in:
Ainar Garipov 2024-11-07 18:53:28 +03:00
parent 35324db80b
commit 391f79b244
2 changed files with 118 additions and 159 deletions

View file

@ -9,8 +9,7 @@ set -e -f -u
# Function log is an echo wrapper that writes to stderr if the caller # Function log is an echo wrapper that writes to stderr if the caller
# requested verbosity level greater than 0. Otherwise, it does nothing. # requested verbosity level greater than 0. Otherwise, it does nothing.
log() { log() {
if [ "$verbose" -gt '0' ] if [ "$verbose" -gt '0' ]; then
then
echo "$1" 1>&2 echo "$1" 1>&2
fi fi
} }
@ -27,7 +26,7 @@ error_exit() {
# #
# TODO(e.burkov): Document each option. # TODO(e.burkov): Document each option.
usage() { usage() {
echo 'install.sh: usage: [-c channel] [-C cpu_type] [-h] [-O os] [-o output_dir]'\ echo 'install.sh: usage: [-c channel] [-C cpu_type] [-h] [-O os] [-o output_dir]' \
'[-r|-R] [-u|-U] [-v|-V]' 1>&2 '[-r|-R] [-u|-U] [-v|-V]' 1>&2
exit 2 exit 2
@ -38,8 +37,7 @@ usage() {
# #
# TODO(e.burkov): Use everywhere the sudo_cmd isn't quoted. # TODO(e.burkov): Use everywhere the sudo_cmd isn't quoted.
maybe_sudo() { maybe_sudo() {
if [ "$use_sudo" -eq 0 ] if [ "$use_sudo" -eq 0 ]; then
then
"$@" "$@"
else else
"$sudo_cmd" "$@" "$sudo_cmd" "$@"
@ -64,8 +62,8 @@ is_little_endian() {
# explicitly implementation-defined in POSIX. Use hexdump instead of od, # explicitly implementation-defined in POSIX. Use hexdump instead of od,
# because OpenWrt and its derivatives have the former but not the latter. # because OpenWrt and its derivatives have the former but not the latter.
is_little_endian_result="$( is_little_endian_result="$(
printf 'I'\ printf 'I' \
| hexdump -o\ | hexdump -o \
| awk '{ print substr($2, 6, 1); exit; }' | awk '{ print substr($2, 6, 1); exit; }'
)" )"
readonly is_little_endian_result readonly is_little_endian_result
@ -84,15 +82,14 @@ check_required() {
required_unix="tar" required_unix="tar"
readonly required_darwin required_unix readonly required_darwin required_unix
case "$os" case "$os" in
in 'freebsd' | 'linux' | 'openbsd')
('freebsd'|'linux'|'openbsd')
required="$required_unix" required="$required_unix"
;; ;;
('darwin') 'darwin')
required="$required_darwin" required="$required_darwin"
;; ;;
(*) *)
# Generally shouldn't happen, since the OS has already been validated. # Generally shouldn't happen, since the OS has already been validated.
error_exit "unsupported operating system: '$os'" error_exit "unsupported operating system: '$os'"
;; ;;
@ -100,11 +97,9 @@ check_required() {
readonly required readonly required
# Don't use quotes to get word splitting. # Don't use quotes to get word splitting.
for cmd in $required for cmd in $required; do
do
log "checking $cmd" log "checking $cmd"
if ! is_command "$cmd" if ! is_command "$cmd"; then
then
log "the full list of required software: [$required]" log "the full list of required software: [$required]"
error_exit "$cmd is required to install AdGuard Home via this script" error_exit "$cmd is required to install AdGuard Home via this script"
@ -114,57 +109,53 @@ check_required() {
# Function check_out_dir requires the output directory to be set and exist. # Function check_out_dir requires the output directory to be set and exist.
check_out_dir() { check_out_dir() {
if [ "$out_dir" = '' ] if [ "$out_dir" = '' ]; then
then
error_exit 'output directory should be presented' error_exit 'output directory should be presented'
fi fi
if ! [ -d "$out_dir" ] if ! [ -d "$out_dir" ]; then
then
log "$out_dir directory will be created" log "$out_dir directory will be created"
fi fi
} }
# Function parse_opts parses the options list and validates it's combinations. # Function parse_opts parses the options list and validates it's combinations.
parse_opts() { parse_opts() {
while getopts "C:c:hO:o:rRuUvV" opt "$@" while getopts "C:c:hO:o:rRuUvV" opt "$@"; do
do case "$opt" in
case "$opt" C)
in
(C)
cpu="$OPTARG" cpu="$OPTARG"
;; ;;
(c) c)
channel="$OPTARG" channel="$OPTARG"
;; ;;
(h) h)
usage usage
;; ;;
(O) O)
os="$OPTARG" os="$OPTARG"
;; ;;
(o) o)
out_dir="$OPTARG" out_dir="$OPTARG"
;; ;;
(R) R)
reinstall='0' reinstall='0'
;; ;;
(U) U)
uninstall='0' uninstall='0'
;; ;;
(r) r)
reinstall='1' reinstall='1'
;; ;;
(u) u)
uninstall='1' uninstall='1'
;; ;;
(V) V)
verbose='0' verbose='0'
;; ;;
(v) v)
verbose='1' verbose='1'
;; ;;
(*) *)
log "bad option $OPTARG" log "bad option $OPTARG"
usage usage
@ -172,8 +163,7 @@ parse_opts() {
esac esac
done done
if [ "$uninstall" -eq '1' ] && [ "$reinstall" -eq '1' ] if [ "$uninstall" -eq '1' ] && [ "$reinstall" -eq '1' ]; then
then
error_exit 'the -r and -u options are mutually exclusive' error_exit 'the -r and -u options are mutually exclusive'
fi fi
} }
@ -181,14 +171,12 @@ parse_opts() {
# Function set_channel sets the channel if needed and validates the value. # Function set_channel sets the channel if needed and validates the value.
set_channel() { set_channel() {
# Validate. # Validate.
case "$channel" case "$channel" in
in 'development' | 'edge' | 'beta' | 'release')
('development'|'edge'|'beta'|'release')
# All is well, go on. # All is well, go on.
;; ;;
(*) *)
error_exit \ error_exit "invalid channel '$channel'
"invalid channel '$channel'
supported values are 'development', 'edge', 'beta', and 'release'" supported values are 'development', 'edge', 'beta', and 'release'"
;; ;;
esac esac
@ -200,36 +188,33 @@ supported values are 'development', 'edge', 'beta', and 'release'"
# Function set_os sets the os if needed and validates the value. # Function set_os sets the os if needed and validates the value.
set_os() { set_os() {
# Set if needed. # Set if needed.
if [ "$os" = '' ] if [ "$os" = '' ]; then
then os="$(uname -s)"
os="$( uname -s )" case "$os" in
case "$os" 'Darwin')
in
('Darwin')
os='darwin' os='darwin'
;; ;;
('FreeBSD') 'FreeBSD')
os='freebsd' os='freebsd'
;; ;;
('Linux') 'Linux')
os='linux' os='linux'
;; ;;
('OpenBSD') 'OpenBSD')
os='openbsd' os='openbsd'
;; ;;
(*) *)
error_exit "unsupported operating system: '$os'" error_exit "unsupported operating system: '$os'"
;; ;;
esac esac
fi fi
# Validate. # Validate.
case "$os" case "$os" in
in 'darwin' | 'freebsd' | 'linux' | 'openbsd')
('darwin'|'freebsd'|'linux'|'openbsd')
# All right, go on. # All right, go on.
;; ;;
(*) *)
error_exit "unsupported operating system: '$os'" error_exit "unsupported operating system: '$os'"
;; ;;
esac esac
@ -241,52 +226,49 @@ set_os() {
# Function set_cpu sets the cpu if needed and validates the value. # Function set_cpu sets the cpu if needed and validates the value.
set_cpu() { set_cpu() {
# Set if needed. # Set if needed.
if [ "$cpu" = '' ] if [ "$cpu" = '' ]; then
then cpu="$(uname -m)"
cpu="$( uname -m )" case "$cpu" in
case "$cpu" 'x86_64' | 'x86-64' | 'x64' | 'amd64')
in
('x86_64'|'x86-64'|'x64'|'amd64')
cpu='amd64' cpu='amd64'
;; ;;
('i386'|'i486'|'i686'|'i786'|'x86') 'i386' | 'i486' | 'i686' | 'i786' | 'x86')
cpu='386' cpu='386'
;; ;;
('armv5l') 'armv5l')
cpu='armv5' cpu='armv5'
;; ;;
('armv6l') 'armv6l')
cpu='armv6' cpu='armv6'
;; ;;
('armv7l' | 'armv8l') 'armv7l' | 'armv8l')
cpu='armv7' cpu='armv7'
;; ;;
('aarch64'|'arm64') 'aarch64' | 'arm64')
cpu='arm64' cpu='arm64'
;; ;;
('mips'|'mips64') 'mips' | 'mips64')
if is_little_endian if is_little_endian; then
then
cpu="${cpu}le" cpu="${cpu}le"
fi fi
cpu="${cpu}_softfloat" cpu="${cpu}_softfloat"
;; ;;
(*) *)
error_exit "unsupported cpu type: $cpu" error_exit "unsupported cpu type: $cpu"
;; ;;
esac esac
fi fi
# Validate. # Validate.
case "$cpu" case "$cpu" in
in 'amd64' | '386' | 'armv5' | 'armv6' | 'armv7' | 'arm64')
('amd64'|'386'|'armv5'|'armv6'|'armv7'|'arm64')
# All right, go on. # All right, go on.
;; ;;
('mips64le_softfloat'|'mips64_softfloat'|'mipsle_softfloat'|'mips_softfloat') 'mips64le_softfloat' | 'mips64_softfloat' | 'mipsle_softfloat' | 'mips_softfloat')
# That's right too. # That's right too.
;; ;;
(*) *)
error_exit "unsupported cpu type: $cpu" error_exit "unsupported cpu type: $cpu"
;; ;;
esac esac
@ -301,8 +283,7 @@ set_cpu() {
# #
# See https://github.com/AdguardTeam/AdGuardHome/issues/2443. # See https://github.com/AdguardTeam/AdGuardHome/issues/2443.
fix_darwin() { fix_darwin() {
if [ "$os" != 'darwin' ] if [ "$os" != 'darwin' ]; then
then
return 0 return 0
fi fi
@ -317,16 +298,14 @@ fix_darwin() {
# Function fix_freebsd performs some fixes to make it work on FreeBSD. # Function fix_freebsd performs some fixes to make it work on FreeBSD.
fix_freebsd() { fix_freebsd() {
if ! [ "$os" = 'freebsd' ] if ! [ "$os" = 'freebsd' ]; then
then
return 0 return 0
fi fi
rcd='/usr/local/etc/rc.d' rcd='/usr/local/etc/rc.d'
readonly rcd readonly rcd
if ! [ -d "$rcd" ] if ! [ -d "$rcd" ]; then
then
mkdir "$rcd" mkdir "$rcd"
fi fi
} }
@ -335,8 +314,7 @@ fix_freebsd() {
# The second argument is optional and is the output file. # The second argument is optional and is the output file.
download_curl() { download_curl() {
curl_output="${2:-}" curl_output="${2:-}"
if [ "$curl_output" = '' ] if [ "$curl_output" = '' ]; then
then
curl -L -S -s "$1" curl -L -S -s "$1"
else else
curl -L -S -o "$curl_output" -s "$1" curl -L -S -o "$curl_output" -s "$1"
@ -355,8 +333,7 @@ download_wget() {
# URL. The second argument is optional and is the output file. # URL. The second argument is optional and is the output file.
download_fetch() { download_fetch() {
fetch_output="${2:-}" fetch_output="${2:-}"
if [ "$fetch_output" = '' ] if [ "$fetch_output" = '' ]; then
then
fetch -o '-' "$1" fetch -o '-' "$1"
else else
fetch -o "$fetch_output" "$1" fetch -o "$fetch_output" "$1"
@ -366,15 +343,12 @@ download_fetch() {
# Function set_download_func sets the appropriate function for downloading # Function set_download_func sets the appropriate function for downloading
# files. # files.
set_download_func() { set_download_func() {
if is_command 'curl' if is_command 'curl'; then
then
# Go on and use the default, download_curl. # Go on and use the default, download_curl.
return 0 return 0
elif is_command 'wget' elif is_command 'wget'; then
then
download_func='download_wget' download_func='download_wget'
elif is_command 'fetch' elif is_command 'fetch'; then
then
download_func='download_fetch' download_func='download_fetch'
else else
error_exit "either curl or wget is required to install AdGuard Home via this script" error_exit "either curl or wget is required to install AdGuard Home via this script"
@ -384,15 +358,14 @@ set_download_func() {
# Function set_sudo_cmd sets the appropriate command to run a command under # Function set_sudo_cmd sets the appropriate command to run a command under
# superuser privileges. # superuser privileges.
set_sudo_cmd() { set_sudo_cmd() {
case "$os" case "$os" in
in 'openbsd')
('openbsd')
sudo_cmd='doas' sudo_cmd='doas'
;; ;;
('darwin'|'freebsd'|'linux') 'darwin' | 'freebsd' | 'linux')
# Go on and use the default, sudo. # Go on and use the default, sudo.
;; ;;
(*) *)
error_exit "unsupported operating system: '$os'" error_exit "unsupported operating system: '$os'"
;; ;;
esac esac
@ -418,22 +391,20 @@ configure() {
# Function is_root checks for root privileges to be granted. # Function is_root checks for root privileges to be granted.
is_root() { is_root() {
if [ "$( id -u )" -eq '0' ] user_id="$(id -u)"
then if [ "$user_id" -eq '0' ]; then
log 'script is executed with root privileges' log 'script is executed with root privileges'
return 0 return 0
fi fi
if is_command "$sudo_cmd" if is_command "$sudo_cmd"; then
then
log 'note that AdGuard Home requires root privileges to install using this script' log 'note that AdGuard Home requires root privileges to install using this script'
return 1 return 1
fi fi
error_exit \ error_exit 'root privileges are required to install AdGuard Home using this script
'root privileges are required to install AdGuard Home using this script
please, restart it with root privileges' please, restart it with root privileges'
} }
@ -443,25 +414,21 @@ please, restart it with root privileges'
# #
# TODO(e.burkov): Try to avoid restarting. # TODO(e.burkov): Try to avoid restarting.
rerun_with_root() { rerun_with_root() {
script_url=\ script_url='https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh'
'https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh'
readonly script_url readonly script_url
r='-R' r='-R'
if [ "$reinstall" -eq '1' ] if [ "$reinstall" -eq '1' ]; then
then
r='-r' r='-r'
fi fi
u='-U' u='-U'
if [ "$uninstall" -eq '1' ] if [ "$uninstall" -eq '1' ]; then
then
u='-u' u='-u'
fi fi
v='-V' v='-V'
if [ "$verbose" -eq '1' ] if [ "$verbose" -eq '1' ]; then
then
v='-v' v='-v'
fi fi
@ -473,7 +440,7 @@ rerun_with_root() {
# producing any output, the latter prints an exit command for the following # producing any output, the latter prints an exit command for the following
# shell to execute to prevent it from getting an empty input and exiting # shell to execute to prevent it from getting an empty input and exiting
# with a zero code in that case. # with a zero code in that case.
{ "$download_func" "$script_url" || echo 'exit 1'; }\ { "$download_func" "$script_url" || echo 'exit 1'; } \
| $sudo_cmd sh -s -- -c "$channel" -C "$cpu" -O "$os" -o "$out_dir" "$r" "$u" "$v" | $sudo_cmd sh -s -- -c "$channel" -C "$cpu" -O "$os" -o "$out_dir" "$r" "$u" "$v"
# Exit the script. Since if the code of the previous pipeline is non-zero, # Exit the script. Since if the code of the previous pipeline is non-zero,
@ -484,10 +451,9 @@ rerun_with_root() {
# Function download downloads the file from the URL and saves it to the # Function download downloads the file from the URL and saves it to the
# specified filepath. # specified filepath.
download() { download() {
log "downloading package from $url -> $pkg_name" log "downloading package from $url to $pkg_name"
if ! "$download_func" "$url" "$pkg_name" if ! "$download_func" "$url" "$pkg_name"; then
then
error_exit "cannot download the package from $url into $pkg_name" error_exit "cannot download the package from $url into $pkg_name"
fi fi
@ -497,25 +463,29 @@ download() {
# Function unpack unpacks the passed archive depending on it's extension. # Function unpack unpacks the passed archive depending on it's extension.
unpack() { unpack() {
log "unpacking package from $pkg_name into $out_dir" log "unpacking package from $pkg_name into $out_dir"
if ! mkdir -m 0700 -p "$out_dir"
then # shellcheck disable=SC2174
if ! mkdir -m 0700 -p "$out_dir"; then
error_exit "cannot create directory $out_dir" error_exit "cannot create directory $out_dir"
fi fi
case "$pkg_ext" case "$pkg_ext" in
in 'zip')
('zip')
unzip "$pkg_name" -d "$out_dir" unzip "$pkg_name" -d "$out_dir"
;; ;;
('tar.gz') 'tar.gz')
tar -C "$out_dir" -f "$pkg_name" -x -z tar -C "$out_dir" -f "$pkg_name" -x -z
;; ;;
(*) *)
error_exit "unexpected package extension: '$pkg_ext'" error_exit "unexpected package extension: '$pkg_ext'"
;; ;;
esac esac
log "successfully unpacked, contents: $( echo; ls -l -A "$agh_dir" )" unpacked_contents="$(
echo
ls -l -A "$agh_dir"
)"
log "successfully unpacked, contents: $unpacked_contents"
rm "$pkg_name" rm "$pkg_name"
} }
@ -523,34 +493,29 @@ unpack() {
# Function handle_existing detects the existing AGH installation and takes care # Function handle_existing detects the existing AGH installation and takes care
# of removing it if needed. # of removing it if needed.
handle_existing() { handle_existing() {
if ! [ -d "$agh_dir" ] if ! [ -d "$agh_dir" ]; then
then
log 'no need to uninstall' log 'no need to uninstall'
if [ "$uninstall" -eq '1' ] if [ "$uninstall" -eq '1' ]; then
then
exit 0 exit 0
fi fi
return 0 return 0
fi fi
if [ "$( ls -1 -A "$agh_dir" )" != '' ] existing_adguard_home="$(ls -1 -A "$agh_dir")"
then if [ "$existing_adguard_home" != '' ]; then
log 'the existing AdGuard Home installation is detected' log 'the existing AdGuard Home installation is detected'
if [ "$reinstall" -ne '1' ] && [ "$uninstall" -ne '1' ] if [ "$reinstall" -ne '1' ] && [ "$uninstall" -ne '1' ]; then
then
error_exit \ error_exit \
"to reinstall/uninstall the AdGuard Home using this script specify one of the '-r' or '-u' flags" "to reinstall/uninstall the AdGuard Home using this script specify one of the '-r' or '-u' flags"
fi fi
# TODO(e.burkov): Remove the stop once v0.107.1 released. # TODO(e.burkov): Remove the stop once v0.107.1 released.
if ( cd "$agh_dir" && ! ./AdGuardHome -s stop || ! ./AdGuardHome -s uninstall ) if (cd "$agh_dir" && ! ./AdGuardHome -s stop || ! ./AdGuardHome -s uninstall); then
then # It doesn't terminate the script since it is possible that AGH just
# It doesn't terminate the script since it is possible # not installed as service but appearing in the directory.
# that AGH just not installed as service but appearing
# in the directory.
log "cannot uninstall AdGuard Home from $agh_dir" log "cannot uninstall AdGuard Home from $agh_dir"
fi fi
@ -559,8 +524,7 @@ handle_existing() {
log 'AdGuard Home was successfully uninstalled' log 'AdGuard Home was successfully uninstalled'
fi fi
if [ "$uninstall" -eq '1' ] if [ "$uninstall" -eq '1' ]; then
then
exit 0 exit 0
fi fi
} }
@ -569,13 +533,11 @@ handle_existing() {
install_service() { install_service() {
# Installing the service as root is required at least on FreeBSD. # Installing the service as root is required at least on FreeBSD.
use_sudo='0' use_sudo='0'
if [ "$os" = 'freebsd' ] if [ "$os" = 'freebsd' ]; then
then
use_sudo='1' use_sudo='1'
fi fi
if ( cd "$agh_dir" && maybe_sudo ./AdGuardHome -s install ) if (cd "$agh_dir" && maybe_sudo ./AdGuardHome -s install); then
then
return 0 return 0
fi fi
@ -583,13 +545,11 @@ install_service() {
rm -r "$agh_dir" rm -r "$agh_dir"
# Some devices detected to have armv7 CPU face the compatibility # Some devices detected to have armv7 CPU face the compatibility issues with
# issues with actual armv7 builds. We should try to install the # actual armv7 builds. We should try to install the armv5 binary instead.
# armv5 binary instead.
# #
# See https://github.com/AdguardTeam/AdGuardHome/issues/2542. # See https://github.com/AdguardTeam/AdGuardHome/issues/2542.
if [ "$cpu" = 'armv7' ] if [ "$cpu" = 'armv7' ]; then
then
cpu='armv5' cpu='armv5'
reinstall='1' reinstall='1'
@ -601,8 +561,6 @@ install_service() {
error_exit 'cannot install AdGuardHome as a service' error_exit 'cannot install AdGuardHome as a service'
} }
# Entrypoint # Entrypoint
# Set default values of configuration variables. # Set default values of configuration variables.
@ -624,8 +582,7 @@ echo 'starting AdGuard Home installation script'
configure configure
check_required check_required
if ! is_root if ! is_root; then
then
rerun_with_root rerun_with_root
fi fi
# Needs rights. # Needs rights.
@ -638,7 +595,7 @@ unpack
install_service install_service
echo "\ printf '%s\n' \
AdGuard Home is now installed and running 'AdGuard Home is now installed and running' \
you can control the service status with the following commands: 'you can control the service status with the following commands:' \
$sudo_cmd ${agh_dir}/AdGuardHome -s start|stop|restart|status|install|uninstall" "$sudo_cmd ${agh_dir}/AdGuardHome -s start|stop|restart|status|install|uninstall"

View file

@ -20,13 +20,15 @@ fi
run_linter -e shfmt --binary-next-line -d -p -s \ run_linter -e shfmt --binary-next-line -d -p -s \
./scripts/hooks/* \ ./scripts/hooks/* \
./scripts/install.sh \
./scripts/make/*.sh \ ./scripts/make/*.sh \
./scripts/snap/*.sh \ ./scripts/snap/*.sh \
./snap/local/*.sh \ ./snap/local/*.sh \
; ;
shellcheck -e 'SC2250' -f 'gcc' -o 'all' -x -- \ shellcheck -e 'SC2250' -e 'SC2310' -f 'gcc' -o 'all' -x -- \
./scripts/hooks/* \ ./scripts/hooks/* \
./scripts/install.sh \
./scripts/make/*.sh \ ./scripts/make/*.sh \
./scripts/snap/*.sh \ ./scripts/snap/*.sh \
./snap/local/*.sh \ ./snap/local/*.sh \