diff --git a/src/install/default.txt b/src/install/default.txt index d90b318..d32cb27 100644 --- a/src/install/default.txt +++ b/src/install/default.txt @@ -1,670 +1,200 @@ -#!/bin/bash - -#=============================================================================== +#!/usr/bin/env bash # -# FILE: default.txt -# -# USAGE: curl https://getcroc.schollz.com | bash -# OR -# wget -qO- https://getcroc.schollz.com | bash -# -# DESCRIPTION: croc Installer Script. +# Adapted from https://github.com/caddyserver/getcaddy.com # -# This script installs croc into a specified prefix. -# Default prefix = /usr/local/bin +# croc Installer Script # -# OPTIONS: -p, --prefix "${INSTALL_PREFIX}" -# Prefix to install croc into. Defaults to /usr/local/bin -# REQUIREMENTS: bash, uname, tar/unzip, curl/wget, sudo (if not run -# as root), install, mktemp, sha256sum/shasum/sha256 +# Homepage: https://schollz.com/software/croc +# Issues: https://github.com/schollz/croc/issues +# Requires: bash, mv, rm, tr, type, curl/wget, base64, sudo (if not root) +# tar (or unzip on OSX and Windows) # -# BUGS: ...hopefully not. Please report. +# This script safely installs Caddy into your PATH (which may require +# password authorization). Use it like this: # -# NOTES: Homepage: https://schollz.com/software/croc -# Issues: https://github.com/schollz/croc/issues +# $ curl https://getcroc.schollz.com | bash +# or +# $ wget -qO- https://getcroc.schollz.com | bash +# +# In automated environments, you may want to run as root. +# If using curl, we recommend using the -fsSL flags. +# +# This should work on Mac, Linux, and BSD systems, and +# hopefully Windows with Cygwin. Please open an issue if +# you notice any bugs. # -# CREATED: 08/10/2019 16:41 -# REVISION: 0.6.0 -#=============================================================================== -set -o nounset # Treat unset variables as an error -#------------------------------------------------------------------------------- -# DEFAULTS -#------------------------------------------------------------------------------- -PREFIX="${PREFIX:-}" -ANDROID_ROOT="${ANDROID_ROOT:-}" +# [[ $- = *i* ]] && echo "Don't source this script!" && return 10 -# Termux on Android has ${PREFIX} set which already ends with '/usr' -if [[ -n "${ANDROID_ROOT}" && -n "${PREFIX}" ]]; then - INSTALL_PREFIX="${PREFIX}/bin" -else - INSTALL_PREFIX="/usr/local/bin" -fi - -#------------------------------------------------------------------------------- -# FUNCTIONS -#------------------------------------------------------------------------------- +install_croc() +{ + trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; exit 1' ERR + install_path="/usr/local/bin" + croc_os="unsupported" + croc_arch="unknown" + croc_arm="" + croc_version="6.1.3" -#--- FUNCTION ---------------------------------------------------------------- -# NAME: print_banner -# DESCRIPTION: Prints a banner -# PARAMETERS: none -# RETURNS: 0 -#------------------------------------------------------------------------------- -print_banner() { - cat <<-'EOF' -================================================= - ____ - / ___|_ __ ___ ___ - | | | '__/ _ \ / __| - | |___| | | (_) | (__ - \____|_| \___/ \___| + # Termux on Android has $PREFIX set which already ends with /usr + if [[ -n "$ANDROID_ROOT" && -n "$PREFIX" ]]; then + install_path="$PREFIX/bin" + fi - ___ _ _ _ - |_ _|_ __ ___| |_ __ _| | | ___ _ __ - | || '_ \/ __| __/ _` | | |/ _ \ '__| - | || | | \__ \ || (_| | | | __/ | - |___|_| |_|___/\__\__,_|_|_|\___|_| -================================================== -EOF + # Fall back to /usr/bin if necessary + if [[ ! -d $install_path ]]; then + install_path="/usr/bin" + fi + + # Not every platform has or needs sudo (https://termux.com/linux.html) + ((EUID)) && [[ -z "$ANDROID_ROOT" ]] && sudo_cmd="sudo" + + ######################### + # Which OS and version? # + ######################### + + croc_bin="croc" + croc_dl_ext=".tar.gz" + + # NOTE: `uname -m` is more accurate and universal than `arch` + # See https://en.wikipedia.org/wiki/Uname + unamem="$(uname -m)" + if [[ $unamem == *aarch64* ]]; then + croc_arch="ARM64" + elif [[ $unamem == *64* ]]; then + croc_arch="64bit" + elif [[ $unamem == *86* ]]; then + croc_arch="32bit" + elif [[ $unamem == *arm* ]]; then + croc_arch="ARM" + else + echo "Aborted, unsupported or unknown architecture: $unamem" + return 2 + fi + + unameu="$(tr '[:lower:]' '[:upper:]' <<<$(uname))" + if [[ $unameu == *DARWIN* ]]; then + croc_os="macOS" + vers=$(sw_vers) + version=${vers##*ProductVersion:} + IFS='.' read OSX_MAJOR OSX_MINOR _ <<<"$version" + + # # Major + # if ((OSX_MAJOR < 10)); then + # echo "Aborted, unsupported OS X version (9-)" + # return 3 + # fi + # if ((OSX_MAJOR > 10)); then + # echo "Aborted, unsupported OS X version (11+)" + # return 4 + # fi + + # # Minor + # if ((OSX_MINOR < 5)); then + # echo "Aborted, unsupported OS X version (10.5-)" + # return 5 + # fi + elif [[ $unameu == *LINUX* ]]; then + croc_os="Linux" + elif [[ $unameu == *FREEBSD* ]]; then + croc_os="FreeBSD" + elif [[ $unameu == *NETBSD* ]]; then + croc_os="NetBSD" + elif [[ $unameu == *OPENBSD* ]]; then + croc_os="OpenBSD" + elif [[ $unameu == *WIN* || $unameu == MSYS* ]]; then + # Should catch cygwin + sudo_cmd="" + croc_os="Windows" + croc_dl_ext=".zip" + croc_bin=$croc_bin.exe + else + echo "Aborted, unsupported or unknown os: $uname" + return 6 + fi + croc_file="croc_${croc_version}_${croc_os}-${croc_arch}${croc_dl_ext}" + + ######################## + # Download and extract # + ######################## + + croc_url="https://github.com/schollz/croc/releases/download/v${croc_version}/${croc_file}" + croc_checksum_url="https://github.com/schollz/croc/releases/download/v${croc_version}/croc_${croc_version}_checksums.txt" + echo "Downloading croc v${croc_version} (${croc_os} ${croc_arch})..." + + type -p gpg >/dev/null 2>&1 && gpg=1 || gpg=0 + + # Use $PREFIX for compatibility with Termux on Android + dl="$PREFIX$croc_file" + dl_checksum="$croc_file.checksum" + rm -rf -- "$dl" + rm -rf -- "$dl_checksum" + + + if type -p curl >/dev/null 2>&1; then + curl -fsSL "$croc_url" -o "$dl" + curl -fsSL "$croc_checksum_url" -o "$dl_checksum" + elif type -p wget >/dev/null 2>&1; then + wget --quiet "$croc_url" -O "$dl" + wget --quiet "$croc_checksum_url" -O "$dl_checksum" + else + echo "Aborted, could not find curl or wget" + return 7 + fi + + echo "Verifying checksum..." + if [[ $unameu == *DARWIN* ]]; then + checksum="$(shasum -a 256 ${dl})" + elif [[ $unameu == "FREEBSD" ]]; then + checksum="$(sha256 -q ${dl}) $croc_file" + else + checksum="$(sha256sum ${dl})" + fi + checksum_check="$(cat ${dl_checksum} | grep "${checksum}")" + + if [[ "${checksum}" != "${checksum_check}" ]]; then + echo "${checksum}" + echo "${checksum_check}" + echo "checksums are not valid, exiting" + return 7 + else + echo "verified checksum" + echo "Downloaded: ${checksum}" + echo "Remote: ${checksum_check}" + fi + + + echo "Extracting..." + case "$croc_file" in + *.zip) unzip -o "$dl" "$croc_bin" -d "$PREFIX/tmp/" ;; + *.tar.gz) tar -xzf "$dl" -C "$PREFIX/tmp/" "$croc_bin" ;; + esac + chmod +x "$PREFIX/tmp/$croc_bin" + + # Back up existing croc, if any found in path + if croc_path="$(type -p "$croc_bin")"; then + croc_backup="${croc_path}_old" + echo "Backing up $croc_path to $croc_backup" + echo "(Password may be required.)" + $sudo_cmd mv "$croc_path" "$croc_backup" + fi + + echo "Putting croc in $install_path (may require password)" + $sudo_cmd mv "$PREFIX/tmp/$croc_bin" "$install_path/$croc_bin" + if setcap_cmd=$(PATH+=$PATH:/sbin type -p setcap); then + $sudo_cmd $setcap_cmd cap_net_bind_service=+ep "$install_path/$croc_bin" + fi + $sudo_cmd rm -- "$dl" + $sudo_cmd rm -- "$dl_checksum" + + # check installation + $croc_bin -version + + echo "Successfully installed" + trap ERR + return 0 } -#--- FUNCTION ---------------------------------------------------------------- -# NAME: print_help -# DESCRIPTION: Prints out a help message -# PARAMETERS: none -# RETURNS: 0 -#------------------------------------------------------------------------------- -print_help() { - local help_header - local help_message - - help_header="croc Installer Script" - help_message="Usage: - -p INSTALL_PREFIX - Prefix to install croc into. Directory must already exist. - Default = /usr/local/bin ('\${PREFIX}/bin' on Termux for Android) - - -h - Prints this helpfull message and exit." - - echo "${help_header}" - echo "" - echo "${help_message}" -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: print_message -# DESCRIPTION: Prints a message all fancy like -# PARAMETERS: $1 = Message to print -# $2 = Severity. info, ok, error, warn -# RETURNS: Formatted Message to stdout -#------------------------------------------------------------------------------- -print_message() { - local message - local severity - local red - local green - local yellow - local nc - - message="${1}" - severity="${2}" - red='\e[0;31m' - green='\e[0;32m' - yellow='\e[1;33m' - nc='\e[0m' - - case "${severity}" in - "info" ) echo -e "${nc}${message}${nc}";; - "ok" ) echo -e "${green}${message}${nc}";; - "error" ) echo -e "${red}${message}${nc}";; - "warn" ) echo -e "${yellow}${message}${nc}";; - esac +install_croc "$@" -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: make_tempdir -# DESCRIPTION: Makes a temp dir using mktemp if available -# PARAMETERS: $1 = Directory template -# RETURNS: 0 = Created temp dir. Also prints temp file path to stdout -# 1 = Failed to create temp dir -# 20 = Failed to find mktemp -#------------------------------------------------------------------------------- -make_tempdir() { - local template - local tempdir - local tempdir_rcode - - template="${1}.XXXXXX" - - if command -v mktemp >/dev/null 2>&1; then - tempdir="$(mktemp -d -t "${template}")" - tempdir_rcode="${?}" - if [[ "${tempdir_rcode}" == "0" ]]; then - echo "${tempdir}" - return 0 - else - return 1 - fi - else - return 20 - fi -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: determine_os -# DESCRIPTION: Attempts to determin host os using uname -# PARAMETERS: none -# RETURNS: 0 = OS Detected. Also prints detected os to stdout -# 1 = Unkown OS -# 20 = 'uname' not found in path -#------------------------------------------------------------------------------- -determine_os() { - local uname_out - - if command -v uname >/dev/null 2>&1; then - uname_out="$(uname)" - if [[ "${uname_out}" == "" ]]; then - return 1 - else - echo "${uname_out}" - return 0 - fi - else - return 20 - fi -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: determine_arch -# DESCRIPTION: Attempt to determin architecture of host -# PARAMETERS: none -# RETURNS: 0 = Arch Detected. Also prints detected arch to stdout -# 1 = Unkown arch -# 20 = 'uname' not found in path -#------------------------------------------------------------------------------- -determine_arch() { - local uname_out - - if command -v uname >/dev/null 2>&1; then - uname_out="$(uname -m)" - if [[ "${uname_out}" == "" ]]; then - return 1 - else - echo "${uname_out}" - return 0 - fi - else - return 20 - fi -} - - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: download_file -# DESCRIPTION: Downloads a file into the specified directory. Attempts to -# use curl, then wget. If neither is found, fail. -# PARAMETERS: $1 = url of file to download -# $2 = location to download file into on host system -# RETURNS: If curl or wget found, returns the return code of curl or wget -# 20 = Could not find curl and wget -#------------------------------------------------------------------------------- -download_file() { - local url - local dir - local filename - local rcode - - url="${1}" - dir="${2}" - filename="${3}" - - if command -v curl >/dev/null 2>&1; then - curl -fsSL "${url}" -o "${dir}/${filename}" - rcode="${?}" - elif command -v wget >/dev/null 2>&1; then - wget --quiet "${url}" -O "${dir}/${filename}" - rcode="${?}" - else - rcode="20" - fi - - return "${rcode}" -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: checksum_check -# DESCRIPTION: Attempt to verify checksum of downloaded file to ensure -# integrity. Tries multiple tools before faling. -# PARAMETERS: $1 = path to checksum file -# $2 = location of file to check -# $3 = working directory -# RETURNS: 0 = checkusm verified -# 1 = checksum verification failed -# 20 = failed to determine tool to use to check checksum -# 30 = failed to change into or go back from working dir -#------------------------------------------------------------------------------- -checksum_check() { - local checksum_file - local file - local dir - local rcode - local shasum_1 - local shasum_2 - local shasum_c - - checksum_file="${1}" - file="${2}" - dir="${3}" - - cd "${dir}" || return 30 - if command -v sha256sum >/dev/null 2>&1; then - ## Not all sha256sum versions seem to have --ignore-missing, so filter the checksum file - ## to only include the file we downloaded. - grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt - shasum_c="$(sha256sum -c "filtered_checksum.txt")" - rcode="${?}" - elif command -v shasum >/dev/null 2>&1; then - ## With shasum on FreeBSD, we don't get to --ignore-missing, so filter the checksum file - ## to only include the file we downloaded. - grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt - shasum_c="$(shasum -a 256 -c "filtered_checksum.txt")" - rcode="${?}" - elif command -v sha256 >/dev/null 2>&1; then - ## With sha256 on FreeBSD, we don't get to --ignore-missing, so filter the checksum file - ## to only include the file we downloaded. - ## Also sha256 -c option seems to fail, so fall back to an if statement - grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt - shasum_1="$(sha256 -q "${file}")" - shasum_2="$(awk '{print $1}' filtered_checksum.txt)" - if [[ "${shasum_1}" == "${shasum_2}" ]]; then - rcode="0" - else - rcode="1" - fi - shasum_c="Expected: ${shasum_1}, Got: ${shasum_2}" - else - return 20 - fi - cd - >/dev/null 2>&1 || return 30 - - if [[ "${rcode}" -gt "0" ]]; then - echo "${shasum_c}" - fi - return "${rcode}" -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: extract_file -# DESCRIPTION: Extracts a file into a location. Attempts to determine which -# tool to use by checking file extention. -# PARAMETERS: $1 = file to extract -# $2 = location to extract file into -# $3 = extention -# RETURNS: Return code of the tool used to extract the file -# 20 = Failed to determine which tool to use -# 30 = Failed to find tool in path -#------------------------------------------------------------------------------- -extract_file() { - local file - local dir - local ext - local rcode - - file="${1}" - dir="${2}" - ext="${3}" - - case "${ext}" in - "zip" ) if command -v unzip >/dev/null 2>&1; then - unzip "${file}" -d "${dir}" - rcode="${?}" - else - rcode="30" - fi - ;; - "tar.gz" ) if command -v tar >/dev/null 2>&1; then - tar -xf "${file}" -C "${dir}" - rcode="${?}" - else - rcode="30" - fi - ;; - * ) rcode="20";; - esac - - return "${rcode}" -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: install_file_freebsd -# DESCRIPTION: Installs a file into a location using 'install'. If EUID not -# 0, then attempt to use sudo. -# PARAMETERS: $1 = file to install -# $2 = location to install file into -# RETURNS: 0 = File Installed -# 1 = File not installed -# 20 = Could not find install command -# 21 = Could not find sudo command -#------------------------------------------------------------------------------- -install_file_freebsd() { - local file - local prefix - local rcode - - file="${1}" - prefix="${2}" - - if command -v install >/dev/null 2>&1; then - if [[ "${EUID}" == "0" ]]; then - install -C -b -B '_old' -m 755 "${file}" "${prefix}" - rcode="${?}" - else - if command -v sudo >/dev/null 2>&1; then - sudo install -C -b -B '_old' -m 755 "${file}" "${prefix}" - rcode="${?}" - else - rcode="21" - fi - fi - else - rcode="20" - fi - - return "${rcode}" -} - - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: install_file_linux -# DESCRIPTION: Installs a file into a location using 'install'. If EUID not -# 0, then attempt to use sudo (unless on android). -# PARAMETERS: $1 = file to install -# $2 = location to install file into -# RETURNS: 0 = File Installed -# 1 = File not installed -# 20 = Could not find install command -# 21 = Could not find sudo command -#------------------------------------------------------------------------------- -install_file_linux() { - local file - local prefix - local rcode - - file="${1}" - prefix="${2}" - - if command -v install >/dev/null 2>&1; then - if [[ "${EUID}" == "0" ]]; then - install -C -b -S '_old' -m 755 -t "${prefix}" "${file}" - rcode="${?}" - else - if command -v sudo >/dev/null 2>&1; then - sudo install -C -b -S '_old' -m 755 "${file}" "${prefix}" - rcode="${?}" - elif [[ "${ANDROID_ROOT}" != "" ]]; then - install -C -b -S '_old' -m 755 -t "${prefix}" "${file}" - rcode="${?}" - else - rcode="21" - fi - fi - else - rcode="20" - fi - - return "${rcode}" -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: install_file_cygwin -# DESCRIPTION: Installs a file into a location using 'install'. If EUID not -# 0, then attempt to use sudo. -# Not really 100% sure this is how to install croc in cygwin. -# PARAMETERS: $1 = file to install -# $2 = location to install file into -# RETURNS: 0 = File Installed -# 20 = Could not find install command -# 21 = Could not find sudo command -#------------------------------------------------------------------------------- -install_file_cygwin() { - local file - local prefix - local rcode - - file="${1}" - prefix="${2}" - - if command -v install >/dev/null 2>&1; then - if [[ "${EUID}" == "0" ]]; then - install -m 755 "${prefix}" "${file}" - rcode="${?}" - else - if command -v sudo >/dev/null 2>&1; then - sudo install -m 755 "${file}" "${prefix}" - rcode="${?}" - else - rcode="21" - fi - fi - else - rcode="20" - fi - - return "${rcode}" -} - -#--- FUNCTION ---------------------------------------------------------------- -# NAME: main -# DESCRIPTION: Put it all together in a logical way -# ...at least that is the hope... -# PARAMETERS: 1 = prefix -# RETURNS: 0 = All good -# 1 = Something done broke -#------------------------------------------------------------------------------- -main() { - local prefix - local tmpdir - local tmpdir_rcode - local croc_arch - local croc_arch_rcode - local croc_os - local croc_os_rcode - local croc_base_url - local croc_url - local croc_file - local croc_checksum_file - local croc_bin_name - local croc_version - local croc_dl_ext - local download_file_rcode - local download_checksum_file_rcode - local checksum_check_rcode - local extract_file_rcode - local install_file_rcode - - croc_bin_name="croc" - croc_version="6.1.1" - croc_dl_ext="tar.gz" - croc_base_url="https://github.com/schollz/croc/releases/download" - prefix="${1}" - - print_banner - print_message "== Install prefix set to ${prefix}" "info" - - tmpdir="$(make_tempdir "${croc_bin_name}")" - tmpdir_rcode="${?}" - if [[ "${tmpdir_rcode}" == "0" ]]; then - print_message "== Created temp dir at ${tmpdir}" "info" - elif [[ "${tmpdir_rcode}" == "1" ]]; then - print_message "== Failed to create temp dir at ${tmpdir}" "error" - else - print_message "== 'mktemp' not found in path. Is it installed?" "error" - exit 1 - fi - - croc_arch="$(determine_arch)" - croc_arch_rcode="${?}" - if [[ "${croc_arch_rcode}" == "0" ]]; then - print_message "== Architecture detected as ${croc_arch}" "info" - elif [[ "${croc_arch_rcode}" == "1" ]]; then - print_message "== Architecture not detected" "error" - exit 1 - else - print_message "== 'uname' not found in path. Is it installed?" "error" - exit 1 - fi - - croc_os="$(determine_os)" - croc_os_rcode="${?}" - if [[ "${croc_os_rcode}" == "0" ]]; then - print_message "== OS detected as ${croc_os}" "info" - elif [[ "${croc_os_rcode}" == "1" ]]; then - print_message "== OS not detected" "error" - exit 1 - else - print_message "== 'uname' not found in path. Is it installed?" "error" - exit 1 - fi - - case "${croc_os}" in - "Darwin" ) croc_os="macOS";; - "CYGWIN"* ) croc_os="Windows"; - croc_dl_ext="zip"; - print_message "== Cygwin is currently unsupported." "error"; - exit 1;; - esac - - case "${croc_arch}" in - "x86_64" ) croc_arch="64bit";; - "amd64" ) croc_arch="64bit";; - "aarch64" ) croc_arch="ARM64";; - "armv7l" ) croc_arch="ARM";; - "i686" ) croc_arch="32bit";; - * ) croc_arch="unknown";; - esac - - croc_file="${croc_bin_name}_${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}" - croc_checksum_file="${croc_bin_name}_${croc_version}_checksums.txt" - croc_url="${croc_base_url}/v${croc_version}/${croc_file}" - croc_checksum_url="${croc_base_url}/v${croc_version}/${croc_checksum_file}" - - download_file "${croc_url}" "${tmpdir}" "${croc_file}" - download_file_rcode="${?}" - if [[ "${download_file_rcode}" == "0" ]]; then - print_message "== Downloaded croc archive into ${tmpdir}" "info" - elif [[ "${download_file_rcode}" == "1" ]]; then - print_message "== Failed to download croc archive" "error" - exit 1 - elif [[ "${download_file_rcode}" == "20" ]]; then - print_message "== Failed to locate curl or wget" "error" - exit 1 - else - print_message "== Return code of download tool returned an unexpected value of ${download_file_rcode}" "error" - exit 1 - fi - download_file "${croc_checksum_url}" "${tmpdir}" "${croc_checksum_file}" - download_checksum_file_rcode="${?}" - if [[ "${download_checksum_file_rcode}" == "0" ]]; then - print_message "== Downloaded croc checksums file into ${tmpdir}" "info" - elif [[ "${download_checksum_file_rcode}" == "1" ]]; then - print_message "== Failed to download croc checksums" "error" - exit 1 - elif [[ "${download_checksum_file_rcode}" == "20" ]]; then - print_message "== Failed to locate curl or wget" "error" - exit 1 - else - print_message "== Return code of download tool returned an unexpected value of ${download_checksum_file_rcode}" "error" - exit 1 - fi - - checksum_check "${tmpdir}/${croc_checksum_file}" "${tmpdir}/${croc_file}" "${tmpdir}" - checksum_check_rcode="${?}" - if [[ "${checksum_check_rcode}" == "0" ]]; then - print_message "== Checksum of ${tmpdir}/${croc_file} verified" "ok" - elif [[ "${checksum_check_rcode}" == "1" ]]; then - print_message "== Failed to verify checksum of ${tmpdir}/${croc_file}" "error" - exit 1 - elif [[ "${checksum_check_rcode}" == "20" ]]; then - print_message "== Failed to find tool to verify sha256 sums" "error" - exit 1 - elif [[ "${checksum_check_rcode}" == "30" ]]; then - print_message "== Failed to change into working directory ${tmpdir}" "error" - exit 1 - else - print_message "== Unknown return code returned while checking checksum of ${tmpdir}/${croc_file}. Returned ${checksum_check_rcode}" "error" - exit 1 - fi - - extract_file "${tmpdir}/${croc_file}" "${tmpdir}/" "${croc_dl_ext}" - extract_file_rcode="${?}" - if [[ "${extract_file_rcode}" == "0" ]]; then - print_message "== Extracted ${croc_file} to ${tmpdir}/" "info" - elif [[ "${extract_file_rcode}" == "1" ]]; then - print_message "== Failed to extract ${croc_file}" "error" - exit 1 - elif [[ "${extract_file_rcode}" == "20" ]]; then - print_message "== Failed to determine which extraction tool to use" "error" - exit 1 - elif [[ "${extract_file_rcode}" == "30" ]]; then - print_message "== Failed to find extraction tool in path" "error" - exit 1 - else - print_message "== Unknown error returned from extraction attempt" "error" - exit 1 - fi - - case "${croc_os}" in - "Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/"; - install_file_rcode="${?}";; - "FreeBSD" ) install_file_freebsd "${tmpdir}/${croc_bin_name}" "${prefix}/"; - install_file_rcode="${?}";; - "macOS" ) install_file_freebsd "${tmpdir}/${croc_bin_name}" "${prefix}/"; - install_file_rcode="${?}";; - "Windows" ) install_file_cygwin "${tmpdir}/${croc_bin_name}" "${prefix}/"; - install_file_rcode="${?}";; - esac - if [[ "${install_file_rcode}" == "0" ]]; then - print_message "== Installed ${croc_bin_name} to ${prefix}/" "ok" - elif [[ "${install_file_rcode}" == "1" ]]; then - print_message "== Failed to install ${croc_bin_name}" "error" - exit 1 - elif [[ "${install_file_rcode}" == "20" ]]; then - print_message "== Failed to locate 'install' command" "error" - exit 1 - elif [[ "${install_file_rcode}" == "21" ]]; then - print_message "== Failed to locate 'sudo' command" "error" - exit 1 - else - print_message "== Install attempt returned an unexpected value of ${install_file_rcode}" "error" - exit 1 - fi - - print_message "== Installation complete" "ok" - - exit 0 -} - -#------------------------------------------------------------------------------- -# ARGUMENT PARSING -#------------------------------------------------------------------------------- -OPTS="hp:" -while getopts "${OPTS}" optchar; do - case "${optchar}" in - 'h' ) print_help - exit 0 - ;; - 'p' ) INSTALL_PREFIX="${OPTARG}" - ;; - /? ) print_message "Unknown option ${OPTARG}" "warn" - ;; - esac -done - -#------------------------------------------------------------------------------- -# CALL MAIN -#------------------------------------------------------------------------------- -main "${INSTALL_PREFIX}" diff --git a/src/install/new_default.txt b/src/install/new_default.txt new file mode 100644 index 0000000..d90b318 --- /dev/null +++ b/src/install/new_default.txt @@ -0,0 +1,670 @@ +#!/bin/bash - +#=============================================================================== +# +# FILE: default.txt +# +# USAGE: curl https://getcroc.schollz.com | bash +# OR +# wget -qO- https://getcroc.schollz.com | bash +# +# DESCRIPTION: croc Installer Script. +# +# This script installs croc into a specified prefix. +# Default prefix = /usr/local/bin +# +# OPTIONS: -p, --prefix "${INSTALL_PREFIX}" +# Prefix to install croc into. Defaults to /usr/local/bin +# REQUIREMENTS: bash, uname, tar/unzip, curl/wget, sudo (if not run +# as root), install, mktemp, sha256sum/shasum/sha256 +# +# BUGS: ...hopefully not. Please report. +# +# NOTES: Homepage: https://schollz.com/software/croc +# Issues: https://github.com/schollz/croc/issues +# +# CREATED: 08/10/2019 16:41 +# REVISION: 0.6.0 +#=============================================================================== +set -o nounset # Treat unset variables as an error + +#------------------------------------------------------------------------------- +# DEFAULTS +#------------------------------------------------------------------------------- +PREFIX="${PREFIX:-}" +ANDROID_ROOT="${ANDROID_ROOT:-}" + +# Termux on Android has ${PREFIX} set which already ends with '/usr' +if [[ -n "${ANDROID_ROOT}" && -n "${PREFIX}" ]]; then + INSTALL_PREFIX="${PREFIX}/bin" +else + INSTALL_PREFIX="/usr/local/bin" +fi + +#------------------------------------------------------------------------------- +# FUNCTIONS +#------------------------------------------------------------------------------- + + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: print_banner +# DESCRIPTION: Prints a banner +# PARAMETERS: none +# RETURNS: 0 +#------------------------------------------------------------------------------- +print_banner() { + cat <<-'EOF' +================================================= + ____ + / ___|_ __ ___ ___ + | | | '__/ _ \ / __| + | |___| | | (_) | (__ + \____|_| \___/ \___| + + ___ _ _ _ + |_ _|_ __ ___| |_ __ _| | | ___ _ __ + | || '_ \/ __| __/ _` | | |/ _ \ '__| + | || | | \__ \ || (_| | | | __/ | + |___|_| |_|___/\__\__,_|_|_|\___|_| +================================================== +EOF +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: print_help +# DESCRIPTION: Prints out a help message +# PARAMETERS: none +# RETURNS: 0 +#------------------------------------------------------------------------------- +print_help() { + local help_header + local help_message + + help_header="croc Installer Script" + help_message="Usage: + -p INSTALL_PREFIX + Prefix to install croc into. Directory must already exist. + Default = /usr/local/bin ('\${PREFIX}/bin' on Termux for Android) + + -h + Prints this helpfull message and exit." + + echo "${help_header}" + echo "" + echo "${help_message}" +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: print_message +# DESCRIPTION: Prints a message all fancy like +# PARAMETERS: $1 = Message to print +# $2 = Severity. info, ok, error, warn +# RETURNS: Formatted Message to stdout +#------------------------------------------------------------------------------- +print_message() { + local message + local severity + local red + local green + local yellow + local nc + + message="${1}" + severity="${2}" + red='\e[0;31m' + green='\e[0;32m' + yellow='\e[1;33m' + nc='\e[0m' + + case "${severity}" in + "info" ) echo -e "${nc}${message}${nc}";; + "ok" ) echo -e "${green}${message}${nc}";; + "error" ) echo -e "${red}${message}${nc}";; + "warn" ) echo -e "${yellow}${message}${nc}";; + esac + + +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: make_tempdir +# DESCRIPTION: Makes a temp dir using mktemp if available +# PARAMETERS: $1 = Directory template +# RETURNS: 0 = Created temp dir. Also prints temp file path to stdout +# 1 = Failed to create temp dir +# 20 = Failed to find mktemp +#------------------------------------------------------------------------------- +make_tempdir() { + local template + local tempdir + local tempdir_rcode + + template="${1}.XXXXXX" + + if command -v mktemp >/dev/null 2>&1; then + tempdir="$(mktemp -d -t "${template}")" + tempdir_rcode="${?}" + if [[ "${tempdir_rcode}" == "0" ]]; then + echo "${tempdir}" + return 0 + else + return 1 + fi + else + return 20 + fi +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: determine_os +# DESCRIPTION: Attempts to determin host os using uname +# PARAMETERS: none +# RETURNS: 0 = OS Detected. Also prints detected os to stdout +# 1 = Unkown OS +# 20 = 'uname' not found in path +#------------------------------------------------------------------------------- +determine_os() { + local uname_out + + if command -v uname >/dev/null 2>&1; then + uname_out="$(uname)" + if [[ "${uname_out}" == "" ]]; then + return 1 + else + echo "${uname_out}" + return 0 + fi + else + return 20 + fi +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: determine_arch +# DESCRIPTION: Attempt to determin architecture of host +# PARAMETERS: none +# RETURNS: 0 = Arch Detected. Also prints detected arch to stdout +# 1 = Unkown arch +# 20 = 'uname' not found in path +#------------------------------------------------------------------------------- +determine_arch() { + local uname_out + + if command -v uname >/dev/null 2>&1; then + uname_out="$(uname -m)" + if [[ "${uname_out}" == "" ]]; then + return 1 + else + echo "${uname_out}" + return 0 + fi + else + return 20 + fi +} + + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: download_file +# DESCRIPTION: Downloads a file into the specified directory. Attempts to +# use curl, then wget. If neither is found, fail. +# PARAMETERS: $1 = url of file to download +# $2 = location to download file into on host system +# RETURNS: If curl or wget found, returns the return code of curl or wget +# 20 = Could not find curl and wget +#------------------------------------------------------------------------------- +download_file() { + local url + local dir + local filename + local rcode + + url="${1}" + dir="${2}" + filename="${3}" + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "${url}" -o "${dir}/${filename}" + rcode="${?}" + elif command -v wget >/dev/null 2>&1; then + wget --quiet "${url}" -O "${dir}/${filename}" + rcode="${?}" + else + rcode="20" + fi + + return "${rcode}" +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: checksum_check +# DESCRIPTION: Attempt to verify checksum of downloaded file to ensure +# integrity. Tries multiple tools before faling. +# PARAMETERS: $1 = path to checksum file +# $2 = location of file to check +# $3 = working directory +# RETURNS: 0 = checkusm verified +# 1 = checksum verification failed +# 20 = failed to determine tool to use to check checksum +# 30 = failed to change into or go back from working dir +#------------------------------------------------------------------------------- +checksum_check() { + local checksum_file + local file + local dir + local rcode + local shasum_1 + local shasum_2 + local shasum_c + + checksum_file="${1}" + file="${2}" + dir="${3}" + + cd "${dir}" || return 30 + if command -v sha256sum >/dev/null 2>&1; then + ## Not all sha256sum versions seem to have --ignore-missing, so filter the checksum file + ## to only include the file we downloaded. + grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt + shasum_c="$(sha256sum -c "filtered_checksum.txt")" + rcode="${?}" + elif command -v shasum >/dev/null 2>&1; then + ## With shasum on FreeBSD, we don't get to --ignore-missing, so filter the checksum file + ## to only include the file we downloaded. + grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt + shasum_c="$(shasum -a 256 -c "filtered_checksum.txt")" + rcode="${?}" + elif command -v sha256 >/dev/null 2>&1; then + ## With sha256 on FreeBSD, we don't get to --ignore-missing, so filter the checksum file + ## to only include the file we downloaded. + ## Also sha256 -c option seems to fail, so fall back to an if statement + grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt + shasum_1="$(sha256 -q "${file}")" + shasum_2="$(awk '{print $1}' filtered_checksum.txt)" + if [[ "${shasum_1}" == "${shasum_2}" ]]; then + rcode="0" + else + rcode="1" + fi + shasum_c="Expected: ${shasum_1}, Got: ${shasum_2}" + else + return 20 + fi + cd - >/dev/null 2>&1 || return 30 + + if [[ "${rcode}" -gt "0" ]]; then + echo "${shasum_c}" + fi + return "${rcode}" +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: extract_file +# DESCRIPTION: Extracts a file into a location. Attempts to determine which +# tool to use by checking file extention. +# PARAMETERS: $1 = file to extract +# $2 = location to extract file into +# $3 = extention +# RETURNS: Return code of the tool used to extract the file +# 20 = Failed to determine which tool to use +# 30 = Failed to find tool in path +#------------------------------------------------------------------------------- +extract_file() { + local file + local dir + local ext + local rcode + + file="${1}" + dir="${2}" + ext="${3}" + + case "${ext}" in + "zip" ) if command -v unzip >/dev/null 2>&1; then + unzip "${file}" -d "${dir}" + rcode="${?}" + else + rcode="30" + fi + ;; + "tar.gz" ) if command -v tar >/dev/null 2>&1; then + tar -xf "${file}" -C "${dir}" + rcode="${?}" + else + rcode="30" + fi + ;; + * ) rcode="20";; + esac + + return "${rcode}" +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: install_file_freebsd +# DESCRIPTION: Installs a file into a location using 'install'. If EUID not +# 0, then attempt to use sudo. +# PARAMETERS: $1 = file to install +# $2 = location to install file into +# RETURNS: 0 = File Installed +# 1 = File not installed +# 20 = Could not find install command +# 21 = Could not find sudo command +#------------------------------------------------------------------------------- +install_file_freebsd() { + local file + local prefix + local rcode + + file="${1}" + prefix="${2}" + + if command -v install >/dev/null 2>&1; then + if [[ "${EUID}" == "0" ]]; then + install -C -b -B '_old' -m 755 "${file}" "${prefix}" + rcode="${?}" + else + if command -v sudo >/dev/null 2>&1; then + sudo install -C -b -B '_old' -m 755 "${file}" "${prefix}" + rcode="${?}" + else + rcode="21" + fi + fi + else + rcode="20" + fi + + return "${rcode}" +} + + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: install_file_linux +# DESCRIPTION: Installs a file into a location using 'install'. If EUID not +# 0, then attempt to use sudo (unless on android). +# PARAMETERS: $1 = file to install +# $2 = location to install file into +# RETURNS: 0 = File Installed +# 1 = File not installed +# 20 = Could not find install command +# 21 = Could not find sudo command +#------------------------------------------------------------------------------- +install_file_linux() { + local file + local prefix + local rcode + + file="${1}" + prefix="${2}" + + if command -v install >/dev/null 2>&1; then + if [[ "${EUID}" == "0" ]]; then + install -C -b -S '_old' -m 755 -t "${prefix}" "${file}" + rcode="${?}" + else + if command -v sudo >/dev/null 2>&1; then + sudo install -C -b -S '_old' -m 755 "${file}" "${prefix}" + rcode="${?}" + elif [[ "${ANDROID_ROOT}" != "" ]]; then + install -C -b -S '_old' -m 755 -t "${prefix}" "${file}" + rcode="${?}" + else + rcode="21" + fi + fi + else + rcode="20" + fi + + return "${rcode}" +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: install_file_cygwin +# DESCRIPTION: Installs a file into a location using 'install'. If EUID not +# 0, then attempt to use sudo. +# Not really 100% sure this is how to install croc in cygwin. +# PARAMETERS: $1 = file to install +# $2 = location to install file into +# RETURNS: 0 = File Installed +# 20 = Could not find install command +# 21 = Could not find sudo command +#------------------------------------------------------------------------------- +install_file_cygwin() { + local file + local prefix + local rcode + + file="${1}" + prefix="${2}" + + if command -v install >/dev/null 2>&1; then + if [[ "${EUID}" == "0" ]]; then + install -m 755 "${prefix}" "${file}" + rcode="${?}" + else + if command -v sudo >/dev/null 2>&1; then + sudo install -m 755 "${file}" "${prefix}" + rcode="${?}" + else + rcode="21" + fi + fi + else + rcode="20" + fi + + return "${rcode}" +} + +#--- FUNCTION ---------------------------------------------------------------- +# NAME: main +# DESCRIPTION: Put it all together in a logical way +# ...at least that is the hope... +# PARAMETERS: 1 = prefix +# RETURNS: 0 = All good +# 1 = Something done broke +#------------------------------------------------------------------------------- +main() { + local prefix + local tmpdir + local tmpdir_rcode + local croc_arch + local croc_arch_rcode + local croc_os + local croc_os_rcode + local croc_base_url + local croc_url + local croc_file + local croc_checksum_file + local croc_bin_name + local croc_version + local croc_dl_ext + local download_file_rcode + local download_checksum_file_rcode + local checksum_check_rcode + local extract_file_rcode + local install_file_rcode + + croc_bin_name="croc" + croc_version="6.1.1" + croc_dl_ext="tar.gz" + croc_base_url="https://github.com/schollz/croc/releases/download" + prefix="${1}" + + print_banner + print_message "== Install prefix set to ${prefix}" "info" + + tmpdir="$(make_tempdir "${croc_bin_name}")" + tmpdir_rcode="${?}" + if [[ "${tmpdir_rcode}" == "0" ]]; then + print_message "== Created temp dir at ${tmpdir}" "info" + elif [[ "${tmpdir_rcode}" == "1" ]]; then + print_message "== Failed to create temp dir at ${tmpdir}" "error" + else + print_message "== 'mktemp' not found in path. Is it installed?" "error" + exit 1 + fi + + croc_arch="$(determine_arch)" + croc_arch_rcode="${?}" + if [[ "${croc_arch_rcode}" == "0" ]]; then + print_message "== Architecture detected as ${croc_arch}" "info" + elif [[ "${croc_arch_rcode}" == "1" ]]; then + print_message "== Architecture not detected" "error" + exit 1 + else + print_message "== 'uname' not found in path. Is it installed?" "error" + exit 1 + fi + + croc_os="$(determine_os)" + croc_os_rcode="${?}" + if [[ "${croc_os_rcode}" == "0" ]]; then + print_message "== OS detected as ${croc_os}" "info" + elif [[ "${croc_os_rcode}" == "1" ]]; then + print_message "== OS not detected" "error" + exit 1 + else + print_message "== 'uname' not found in path. Is it installed?" "error" + exit 1 + fi + + case "${croc_os}" in + "Darwin" ) croc_os="macOS";; + "CYGWIN"* ) croc_os="Windows"; + croc_dl_ext="zip"; + print_message "== Cygwin is currently unsupported." "error"; + exit 1;; + esac + + case "${croc_arch}" in + "x86_64" ) croc_arch="64bit";; + "amd64" ) croc_arch="64bit";; + "aarch64" ) croc_arch="ARM64";; + "armv7l" ) croc_arch="ARM";; + "i686" ) croc_arch="32bit";; + * ) croc_arch="unknown";; + esac + + croc_file="${croc_bin_name}_${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}" + croc_checksum_file="${croc_bin_name}_${croc_version}_checksums.txt" + croc_url="${croc_base_url}/v${croc_version}/${croc_file}" + croc_checksum_url="${croc_base_url}/v${croc_version}/${croc_checksum_file}" + + download_file "${croc_url}" "${tmpdir}" "${croc_file}" + download_file_rcode="${?}" + if [[ "${download_file_rcode}" == "0" ]]; then + print_message "== Downloaded croc archive into ${tmpdir}" "info" + elif [[ "${download_file_rcode}" == "1" ]]; then + print_message "== Failed to download croc archive" "error" + exit 1 + elif [[ "${download_file_rcode}" == "20" ]]; then + print_message "== Failed to locate curl or wget" "error" + exit 1 + else + print_message "== Return code of download tool returned an unexpected value of ${download_file_rcode}" "error" + exit 1 + fi + download_file "${croc_checksum_url}" "${tmpdir}" "${croc_checksum_file}" + download_checksum_file_rcode="${?}" + if [[ "${download_checksum_file_rcode}" == "0" ]]; then + print_message "== Downloaded croc checksums file into ${tmpdir}" "info" + elif [[ "${download_checksum_file_rcode}" == "1" ]]; then + print_message "== Failed to download croc checksums" "error" + exit 1 + elif [[ "${download_checksum_file_rcode}" == "20" ]]; then + print_message "== Failed to locate curl or wget" "error" + exit 1 + else + print_message "== Return code of download tool returned an unexpected value of ${download_checksum_file_rcode}" "error" + exit 1 + fi + + checksum_check "${tmpdir}/${croc_checksum_file}" "${tmpdir}/${croc_file}" "${tmpdir}" + checksum_check_rcode="${?}" + if [[ "${checksum_check_rcode}" == "0" ]]; then + print_message "== Checksum of ${tmpdir}/${croc_file} verified" "ok" + elif [[ "${checksum_check_rcode}" == "1" ]]; then + print_message "== Failed to verify checksum of ${tmpdir}/${croc_file}" "error" + exit 1 + elif [[ "${checksum_check_rcode}" == "20" ]]; then + print_message "== Failed to find tool to verify sha256 sums" "error" + exit 1 + elif [[ "${checksum_check_rcode}" == "30" ]]; then + print_message "== Failed to change into working directory ${tmpdir}" "error" + exit 1 + else + print_message "== Unknown return code returned while checking checksum of ${tmpdir}/${croc_file}. Returned ${checksum_check_rcode}" "error" + exit 1 + fi + + extract_file "${tmpdir}/${croc_file}" "${tmpdir}/" "${croc_dl_ext}" + extract_file_rcode="${?}" + if [[ "${extract_file_rcode}" == "0" ]]; then + print_message "== Extracted ${croc_file} to ${tmpdir}/" "info" + elif [[ "${extract_file_rcode}" == "1" ]]; then + print_message "== Failed to extract ${croc_file}" "error" + exit 1 + elif [[ "${extract_file_rcode}" == "20" ]]; then + print_message "== Failed to determine which extraction tool to use" "error" + exit 1 + elif [[ "${extract_file_rcode}" == "30" ]]; then + print_message "== Failed to find extraction tool in path" "error" + exit 1 + else + print_message "== Unknown error returned from extraction attempt" "error" + exit 1 + fi + + case "${croc_os}" in + "Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/"; + install_file_rcode="${?}";; + "FreeBSD" ) install_file_freebsd "${tmpdir}/${croc_bin_name}" "${prefix}/"; + install_file_rcode="${?}";; + "macOS" ) install_file_freebsd "${tmpdir}/${croc_bin_name}" "${prefix}/"; + install_file_rcode="${?}";; + "Windows" ) install_file_cygwin "${tmpdir}/${croc_bin_name}" "${prefix}/"; + install_file_rcode="${?}";; + esac + if [[ "${install_file_rcode}" == "0" ]]; then + print_message "== Installed ${croc_bin_name} to ${prefix}/" "ok" + elif [[ "${install_file_rcode}" == "1" ]]; then + print_message "== Failed to install ${croc_bin_name}" "error" + exit 1 + elif [[ "${install_file_rcode}" == "20" ]]; then + print_message "== Failed to locate 'install' command" "error" + exit 1 + elif [[ "${install_file_rcode}" == "21" ]]; then + print_message "== Failed to locate 'sudo' command" "error" + exit 1 + else + print_message "== Install attempt returned an unexpected value of ${install_file_rcode}" "error" + exit 1 + fi + + print_message "== Installation complete" "ok" + + exit 0 +} + +#------------------------------------------------------------------------------- +# ARGUMENT PARSING +#------------------------------------------------------------------------------- +OPTS="hp:" +while getopts "${OPTS}" optchar; do + case "${optchar}" in + 'h' ) print_help + exit 0 + ;; + 'p' ) INSTALL_PREFIX="${OPTARG}" + ;; + /? ) print_message "Unknown option ${OPTARG}" "warn" + ;; + esac +done + +#------------------------------------------------------------------------------- +# CALL MAIN +#------------------------------------------------------------------------------- +main "${INSTALL_PREFIX}"