#!/bin/sh # shellcheck shell=dash # This script is shamelessly adapted from the rustup project (https://github.com/rust-lang/rustup/blob/master/rustup-init.sh). # The full license file with all attributions can be found at https://github.com/memflow/sh.memflow.io/blob/main/LICENSE # This is just a little script that can be downloaded from the internet to # install memflowup. It just does platform detection, downloads the installer # and runs it. # It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local` # extension. Note: Most shells limit `local` to 1 var per line, contra bash. if [ "$KSH_VERSION" = 'Version JM 93t+ 2010-03-05' ]; then # The version of ksh93 that ships with many illumos systems does not # support the "local" extension. Print a message rather than fail in # subtle ways later on: echo 'memflowup does not work with this ksh93 version; please try bash!' >&2 exit 1 fi set -u main() { # check existence of basic utilities need_cmd sh need_cmd whoami need_cmd sudo need_cmd cp need_cmd chmod # The installer is going to want to ask for confirmation by # reading stdin. This script was piped into `sh` though and # doesn't have stdin to pass to its children. Instead we're going # to explicitly connect /dev/tty to the installer's stdin. if [ ! -t 1 ]; then err "Unable to run interactively. Run with -y to accept defaults, --help for additional options" fi # check if user ran the script as root if [ "$(id -u)" -eq 0 ]; then warn "The installation script has been started as the root user ($(whoami)) or via sudo. By default rustup installs cargo in your home directory." warn "memflowup installation will only succeed if you also installed rust/cargo as $(whoami)." warn "memflowup will also only be available when logged in to $(whoami)." ask "Do you really want to continue the memflowup installation as root?" fi # just ensure to source cargo env . "$HOME/.cargo/env" > /dev/null 2>&1 # check if cargo is properly setup if ! echo "$PATH" | grep '.cargo/bin'; then say "The .cargo/bin/ directory was not found in your path. Please make sure the \$PATH variable was updated properly after installing rust/cargo." err "If you did not install rust/cargo for the current user ($(whoami)) yet we recommend heading to https://rustup.rs/ and installing rust/cargo via rustup." fi # check compiler specific tools need_cmd rustc need_cmd cargo need_cmd cc need_cmd git if semver_lt "$(get_cargo_version)" "1.51.0"; then warn "The detected cargo version does not meet the minimum requirements to compile memflow." warn "memflow currently requires a minimum cargo of version of 1.51.0, however the detected version on your system is $(get_cargo_version)" ask "Do you really want to continue with a potentially incompatible rust version?" fi get_architecture || return 1 local _arch="$RETVAL" assert_nz "$_arch" "arch" local _ext="" case "$_arch" in *windows*) _ext=".exe" ;; esac # Install memflowup via cargo cargo install memflowup --force # Display the memflowup help local _file="memflowup${_ext}" ignore "$_file" help "$@" < /dev/tty } check_proc() { # Check for /proc by looking for the /proc/self/exe link # This is only run on Linux if ! test -L /proc/self/exe ; then err "fatal: Unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc." fi } get_bitness() { need_cmd head # Architecture detection without dependencies beyond coreutils. # ELF files start out "\x7fELF", and the following byte is # 0x01 for 32-bit and # 0x02 for 64-bit. # The printf builtin on some shells like dash only supports octal # escape sequences, so we use those. local _current_exe_head _current_exe_head=$(head -c 5 /proc/self/exe ) if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then echo 32 elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then echo 64 else err "unknown platform bitness" fi } get_architecture() { local _ostype _cputype _bitness _arch _clibtype _ostype="$(uname -s)" _cputype="$(uname -m)" _clibtype="gnu" if [ "$_ostype" = Linux ]; then if [ "$(uname -o)" = Android ]; then _ostype=Android fi if ldd --version 2>&1 | grep -q 'musl'; then _clibtype="musl" fi fi if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then # Darwin `uname -m` lies if sysctl hw.optional.x86_64 | grep -q ': 1'; then _cputype=x86_64 fi fi if [ "$_ostype" = SunOS ]; then # Both Solaris and illumos presently announce as "SunOS" in "uname -s" # so use "uname -o" to disambiguate. We use the full path to the # system uname in case the user has coreutils uname first in PATH, # which has historically sometimes printed the wrong value here. if [ "$(/usr/bin/uname -o)" = illumos ]; then _ostype=illumos fi # illumos systems have multi-arch userlands, and "uname -m" reports the # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86 # systems. Check for the native (widest) instruction set on the # running kernel: if [ "$_cputype" = i86pc ]; then _cputype="$(isainfo -n)" fi fi case "$_ostype" in Android) _ostype=linux-android ;; Linux) check_proc _ostype=unknown-linux-$_clibtype _bitness=$(get_bitness) ;; FreeBSD) _ostype=unknown-freebsd ;; NetBSD) _ostype=unknown-netbsd ;; DragonFly) _ostype=unknown-dragonfly ;; Darwin) _ostype=apple-darwin ;; illumos) _ostype=unknown-illumos ;; MINGW* | MSYS* | CYGWIN*) _ostype=pc-windows-gnu ;; *) err "unrecognized OS type: $_ostype" ;; esac case "$_cputype" in i386 | i486 | i686 | i786 | x86) _cputype=i686 ;; xscale | arm) _cputype=arm if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi fi ;; armv6l) _cputype=arm if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; armv7l | armv8l) _cputype=armv7 if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; aarch64 | arm64) _cputype=aarch64 ;; x86_64 | x86-64 | x64 | amd64) _cputype=x86_64 ;; mips) _cputype=$(get_endianness mips '' el) ;; mips64) if [ "$_bitness" -eq 64 ]; then # only n64 ABI is supported for now _ostype="${_ostype}abi64" _cputype=$(get_endianness mips64 '' el) fi ;; ppc) _cputype=powerpc ;; ppc64) _cputype=powerpc64 ;; ppc64le) _cputype=powerpc64le ;; s390x) _cputype=s390x ;; riscv64) _cputype=riscv64gc ;; *) err "unknown CPU type: $_cputype" esac # Detect 64-bit linux with 32-bit userland if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then case $_cputype in x86_64) if [ -n "${MEMFLOWUP_CPUTYPE:-}" ]; then _cputype="$MEMFLOWUP_CPUTYPE" else { # 32-bit executable for amd64 = x32 if is_host_amd64_elf; then { echo "This host is running an x32 userland; as it stands, x32 support is poor," 1>&2 echo "and there isn't a native toolchain -- you will have to install" 1>&2 echo "multiarch compatibility with i686 and/or amd64, then select one" 1>&2 echo "by re-running this script with the MEMFLOWUP environment variable" 1>&2 echo "set to i686 or x86_64, respectively." 1>&2 echo 1>&2 echo "You will be able to add an x32 target after installation by running" 1>&2 echo " memflowup target add x86_64-unknown-linux-gnux32" 1>&2 exit 1 }; else _cputype=i686 fi }; fi ;; mips64) _cputype=$(get_endianness mips '' el) ;; powerpc64) _cputype=powerpc ;; aarch64) _cputype=armv7 if [ "$_ostype" = "linux-android" ]; then _ostype=linux-androideabi else _ostype="${_ostype}eabihf" fi ;; riscv64gc) err "riscv64 with 32-bit userland unsupported" ;; esac fi # Detect armv7 but without the CPU features Rust needs in that build, # and fall back to arm. # See https://github.com/rust-lang/rustup.rs/issues/587. if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then # At least one processor does not have NEON. _cputype=arm fi fi _arch="${_cputype}-${_ostype}" RETVAL="$_arch" } say() { printf '%s\n' "$1" } err() { say "$1" >&2 exit 1 } warn() { say "$1" >&2 } ask() { need_cmd read need_cmd grep need_cmd tr local _response read -r -p "$1 [y/N] " _response < /dev/tty _response=$(echo "$_response" | tr '[:upper:]' '[:lower:]') if ! echo "$_response" | grep -E "^(yes|y| )"; then err "user abort" fi } need_cmd() { if ! check_cmd "$1"; then err "need '$1' (command not found)" fi } check_cmd() { command -v "$1" > /dev/null 2>&1 } assert_nz() { if [ -z "$1" ]; then err "assert_nz $2"; fi } # Run a command that should never fail. If the command fails execution # will immediately terminate with an error showing the failing # command. ensure() { if ! "$@"; then err "command failed: $*"; fi } # This is just for indicating that commands' results are being # intentionally ignored. Usually, because it's being executed # as part of error handling. ignore() { "$@" } # Returns the currently installed cargo version get_cargo_version() { need_cmd cut cargo --version | cut -d' ' -f2 } # The semantic versioning check has been vendored from Józef Sokołowski (https://github.com/qzb/sh-semver) get_number() { echo "${1%%-*}" } # Gets prerelase part from normalized version get_prerelease() { local pre_and_meta=${1%+*} local pre=${pre_and_meta#*-} if [ "$pre" = "$1" ]; then echo else echo "$pre" fi } semver_lt() { local number_a number_b prerelease_a prerelease_b number_a=$(get_number "$1") number_b=$(get_number "$2") prerelease_a=$(get_prerelease "$1") prerelease_b=$(get_prerelease "$2") local head_a='' local head_b='' local rest_a=$number_a. local rest_b=$number_b. while [ -n "$rest_a" ] || [ -n "$rest_b" ]; do head_a=${rest_a%%.*} head_b=${rest_b%%.*} rest_a=${rest_a#*.} rest_b=${rest_b#*.} if [ -z "$head_a" ] || [ -z "$head_b" ]; then return 1 fi if [ "$head_a" -eq "$head_b" ]; then continue fi if [ "$head_a" -lt "$head_b" ]; then return 0 else return 1 fi done if [ -n "$prerelease_a" ] && [ -z "$prerelease_b" ]; then return 0 elif [ -z "$prerelease_a" ] && [ -n "$prerelease_b" ]; then return 1 fi local head_a='' local head_b='' local rest_a=$prerelease_a. local rest_b=$prerelease_b. while [ -n "$rest_a" ] || [ -n "$rest_b" ]; do head_a=${rest_a%%.*} head_b=${rest_b%%.*} rest_a=${rest_a#*.} rest_b=${rest_b#*.} if [ -z "$head_a" ] && [ -n "$head_b" ]; then return 0 elif [ -n "$head_a" ] && [ -z "$head_b" ]; then return 1 fi if [ "$head_a" = "$head_b" ]; then continue fi # If both are numbers then compare numerically if [ "$head_a" = "${head_a%[!0-9]*}" ] && [ "$head_b" = "${head_b%[!0-9]*}" ]; then [ "$head_a" -lt "$head_b" ] && return 0 || return 1 # If only a is a number then return true (number has lower precedence than strings) elif [ "$head_a" = "${head_a%[!0-9]*}" ]; then return 0 # If only b is a number then return false elif [ "$head_b" = "${head_b%[!0-9]*}" ]; then return 1 # Finally if of identifiers is a number compare them lexically else test "$head_a" \< "$head_b" && return 0 || return 1 fi done return 1 } main "$@" || exit 1