1#!/bin/sh 2# shellcheck shell=dash 3 4# This is just a little script that can be downloaded from the internet to 5# install rustup. It just does platform detection, downloads the installer 6# and runs it. 7 8# It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local` 9# extension. Note: Most shells limit `local` to 1 var per line, contra bash. 10 11if [ "$KSH_VERSION" = 'Version JM 93t+ 2010-03-05' ]; then 12 # The version of ksh93 that ships with many illumos systems does not 13 # support the "local" extension. Print a message rather than fail in 14 # subtle ways later on: 15 echo 'rustup does not work with this ksh93 version; please try bash!' >&2 16 exit 1 17fi 18 19 20set -u 21 22# If RUSTUP_UPDATE_ROOT is unset or empty, default it. 23RUSTUP_UPDATE_ROOT="${RUSTUP_UPDATE_ROOT:-https://static.rust-lang.org/rustup}" 24 25#XXX: If you change anything here, please make the same changes in setup_mode.rs 26usage() { 27 cat 1>&2 <<EOF 28rustup-init 1.24.3 (c1c769109 2021-05-31) 29The installer for rustup 30 31USAGE: 32 rustup-init [FLAGS] [OPTIONS] 33 34FLAGS: 35 -v, --verbose Enable verbose output 36 -q, --quiet Disable progress output 37 -y Disable confirmation prompt. 38 --no-modify-path Don't configure the PATH environment variable 39 -h, --help Prints help information 40 -V, --version Prints version information 41 42OPTIONS: 43 --default-host <default-host> Choose a default host triple 44 --default-toolchain <default-toolchain> Choose a default toolchain to install 45 --default-toolchain none Do not install any toolchains 46 --profile [minimal|default|complete] Choose a profile 47 -c, --component <components>... Component name to also install 48 -t, --target <targets>... Target name to also install 49EOF 50} 51 52main() { 53 downloader --check 54 need_cmd uname 55 need_cmd mktemp 56 need_cmd chmod 57 need_cmd mkdir 58 need_cmd rm 59 need_cmd rmdir 60 61 get_architecture || return 1 62 local _arch="$RETVAL" 63 assert_nz "$_arch" "arch" 64 65 local _ext="" 66 case "$_arch" in 67 *windows*) 68 _ext=".exe" 69 ;; 70 esac 71 72 local _url="${RUSTUP_UPDATE_ROOT}/dist/${_arch}/rustup-init${_ext}" 73 74 local _dir 75 _dir="$(ensure mktemp -d)" 76 local _file="${_dir}/rustup-init${_ext}" 77 78 local _ansi_escapes_are_valid=false 79 if [ -t 2 ]; then 80 if [ "${TERM+set}" = 'set' ]; then 81 case "$TERM" in 82 xterm*|rxvt*|urxvt*|linux*|vt*) 83 _ansi_escapes_are_valid=true 84 ;; 85 esac 86 fi 87 fi 88 89 # check if we have to use /dev/tty to prompt the user 90 local need_tty=yes 91 for arg in "$@"; do 92 case "$arg" in 93 -h|--help) 94 usage 95 exit 0 96 ;; 97 -y) 98 # user wants to skip the prompt -- we don't need /dev/tty 99 need_tty=no 100 ;; 101 *) 102 ;; 103 esac 104 done 105 106 if $_ansi_escapes_are_valid; then 107 printf "\33[1minfo:\33[0m downloading installer\n" 1>&2 108 else 109 printf '%s\n' 'info: downloading installer' 1>&2 110 fi 111 112 ensure mkdir -p "$_dir" 113 ensure downloader "$_url" "$_file" "$_arch" 114 ensure chmod u+x "$_file" 115 if [ ! -x "$_file" ]; then 116 printf '%s\n' "Cannot execute $_file (likely because of mounting /tmp as noexec)." 1>&2 117 printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./rustup-init${_ext}." 1>&2 118 exit 1 119 fi 120 121 if [ "$need_tty" = "yes" ]; then 122 # The installer is going to want to ask for confirmation by 123 # reading stdin. This script was piped into `sh` though and 124 # doesn't have stdin to pass to its children. Instead we're going 125 # to explicitly connect /dev/tty to the installer's stdin. 126 if [ ! -t 1 ]; then 127 err "Unable to run interactively. Run with -y to accept defaults, --help for additional options" 128 fi 129 130 ignore "$_file" "$@" < /dev/tty 131 else 132 ignore "$_file" "$@" 133 fi 134 135 local _retval=$? 136 137 ignore rm "$_file" 138 ignore rmdir "$_dir" 139 140 return "$_retval" 141} 142 143check_proc() { 144 # Check for /proc by looking for the /proc/self/exe link 145 # This is only run on Linux 146 if ! test -L /proc/self/exe ; then 147 err "fatal: Unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc." 148 fi 149} 150 151get_bitness() { 152 need_cmd head 153 # Architecture detection without dependencies beyond coreutils. 154 # ELF files start out "\x7fELF", and the following byte is 155 # 0x01 for 32-bit and 156 # 0x02 for 64-bit. 157 # The printf builtin on some shells like dash only supports octal 158 # escape sequences, so we use those. 159 local _current_exe_head 160 _current_exe_head=$(head -c 5 /proc/self/exe ) 161 if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then 162 echo 32 163 elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then 164 echo 64 165 else 166 err "unknown platform bitness" 167 fi 168} 169 170is_host_amd64_elf() { 171 need_cmd head 172 need_cmd tail 173 # ELF e_machine detection without dependencies beyond coreutils. 174 # Two-byte field at offset 0x12 indicates the CPU, 175 # but we're interested in it being 0x3E to indicate amd64, or not that. 176 local _current_exe_machine 177 _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1) 178 [ "$_current_exe_machine" = "$(printf '\076')" ] 179} 180 181get_endianness() { 182 local cputype=$1 183 local suffix_eb=$2 184 local suffix_el=$3 185 186 # detect endianness without od/hexdump, like get_bitness() does. 187 need_cmd head 188 need_cmd tail 189 190 local _current_exe_endianness 191 _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" 192 if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then 193 echo "${cputype}${suffix_el}" 194 elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then 195 echo "${cputype}${suffix_eb}" 196 else 197 err "unknown platform endianness" 198 fi 199} 200 201get_architecture() { 202 local _ostype _cputype _bitness _arch _clibtype 203 _ostype="$(uname -s)" 204 _cputype="$(uname -m)" 205 _clibtype="gnu" 206 207 if [ "$_ostype" = Linux ]; then 208 if [ "$(uname -o)" = Android ]; then 209 _ostype=Android 210 fi 211 if ldd --version 2>&1 | grep -q 'musl'; then 212 _clibtype="musl" 213 fi 214 fi 215 216 if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then 217 # Darwin `uname -m` lies 218 if sysctl hw.optional.x86_64 | grep -q ': 1'; then 219 _cputype=x86_64 220 fi 221 fi 222 223 if [ "$_ostype" = SunOS ]; then 224 # Both Solaris and illumos presently announce as "SunOS" in "uname -s" 225 # so use "uname -o" to disambiguate. We use the full path to the 226 # system uname in case the user has coreutils uname first in PATH, 227 # which has historically sometimes printed the wrong value here. 228 if [ "$(/usr/bin/uname -o)" = illumos ]; then 229 _ostype=illumos 230 fi 231 232 # illumos systems have multi-arch userlands, and "uname -m" reports the 233 # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86 234 # systems. Check for the native (widest) instruction set on the 235 # running kernel: 236 if [ "$_cputype" = i86pc ]; then 237 _cputype="$(isainfo -n)" 238 fi 239 fi 240 241 case "$_ostype" in 242 243 Android) 244 _ostype=linux-android 245 ;; 246 247 Linux) 248 check_proc 249 _ostype=unknown-linux-$_clibtype 250 _bitness=$(get_bitness) 251 ;; 252 253 FreeBSD) 254 _ostype=unknown-freebsd 255 ;; 256 257 NetBSD) 258 _ostype=unknown-netbsd 259 ;; 260 261 DragonFly) 262 _ostype=unknown-dragonfly 263 ;; 264 265 Darwin) 266 _ostype=apple-darwin 267 ;; 268 269 illumos) 270 _ostype=unknown-illumos 271 ;; 272 273 MINGW* | MSYS* | CYGWIN*) 274 _ostype=pc-windows-gnu 275 ;; 276 277 *) 278 err "unrecognized OS type: $_ostype" 279 ;; 280 281 esac 282 283 case "$_cputype" in 284 285 i386 | i486 | i686 | i786 | x86) 286 _cputype=i686 287 ;; 288 289 xscale | arm) 290 _cputype=arm 291 if [ "$_ostype" = "linux-android" ]; then 292 _ostype=linux-androideabi 293 fi 294 ;; 295 296 armv6l) 297 _cputype=arm 298 if [ "$_ostype" = "linux-android" ]; then 299 _ostype=linux-androideabi 300 else 301 _ostype="${_ostype}eabihf" 302 fi 303 ;; 304 305 armv7l | armv8l) 306 _cputype=armv7 307 if [ "$_ostype" = "linux-android" ]; then 308 _ostype=linux-androideabi 309 else 310 _ostype="${_ostype}eabihf" 311 fi 312 ;; 313 314 aarch64 | arm64) 315 _cputype=aarch64 316 ;; 317 318 x86_64 | x86-64 | x64 | amd64) 319 _cputype=x86_64 320 ;; 321 322 mips) 323 _cputype=$(get_endianness mips '' el) 324 ;; 325 326 mips64) 327 if [ "$_bitness" -eq 64 ]; then 328 # only n64 ABI is supported for now 329 _ostype="${_ostype}abi64" 330 _cputype=$(get_endianness mips64 '' el) 331 fi 332 ;; 333 334 ppc) 335 _cputype=powerpc 336 ;; 337 338 ppc64) 339 _cputype=powerpc64 340 ;; 341 342 ppc64le) 343 _cputype=powerpc64le 344 ;; 345 346 s390x) 347 _cputype=s390x 348 ;; 349 riscv64) 350 _cputype=riscv64gc 351 ;; 352 *) 353 err "unknown CPU type: $_cputype" 354 355 esac 356 357 # Detect 64-bit linux with 32-bit userland 358 if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then 359 case $_cputype in 360 x86_64) 361 if [ -n "${RUSTUP_CPUTYPE:-}" ]; then 362 _cputype="$RUSTUP_CPUTYPE" 363 else { 364 # 32-bit executable for amd64 = x32 365 if is_host_amd64_elf; then { 366 echo "This host is running an x32 userland; as it stands, x32 support is poor," 1>&2 367 echo "and there isn't a native toolchain -- you will have to install" 1>&2 368 echo "multiarch compatibility with i686 and/or amd64, then select one" 1>&2 369 echo "by re-running this script with the RUSTUP_CPUTYPE environment variable" 1>&2 370 echo "set to i686 or x86_64, respectively." 1>&2 371 echo 1>&2 372 echo "You will be able to add an x32 target after installation by running" 1>&2 373 echo " rustup target add x86_64-unknown-linux-gnux32" 1>&2 374 exit 1 375 }; else 376 _cputype=i686 377 fi 378 }; fi 379 ;; 380 mips64) 381 _cputype=$(get_endianness mips '' el) 382 ;; 383 powerpc64) 384 _cputype=powerpc 385 ;; 386 aarch64) 387 _cputype=armv7 388 if [ "$_ostype" = "linux-android" ]; then 389 _ostype=linux-androideabi 390 else 391 _ostype="${_ostype}eabihf" 392 fi 393 ;; 394 riscv64gc) 395 err "riscv64 with 32-bit userland unsupported" 396 ;; 397 esac 398 fi 399 400 # Detect armv7 but without the CPU features Rust needs in that build, 401 # and fall back to arm. 402 # See https://github.com/rust-lang/rustup.rs/issues/587. 403 if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then 404 if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then 405 # At least one processor does not have NEON. 406 _cputype=arm 407 fi 408 fi 409 410 _arch="${_cputype}-${_ostype}" 411 412 RETVAL="$_arch" 413} 414 415say() { 416 printf 'rustup: %s\n' "$1" 417} 418 419err() { 420 say "$1" >&2 421 exit 1 422} 423 424need_cmd() { 425 if ! check_cmd "$1"; then 426 err "need '$1' (command not found)" 427 fi 428} 429 430check_cmd() { 431 command -v "$1" > /dev/null 2>&1 432} 433 434assert_nz() { 435 if [ -z "$1" ]; then err "assert_nz $2"; fi 436} 437 438# Run a command that should never fail. If the command fails execution 439# will immediately terminate with an error showing the failing 440# command. 441ensure() { 442 if ! "$@"; then err "command failed: $*"; fi 443} 444 445# This is just for indicating that commands' results are being 446# intentionally ignored. Usually, because it's being executed 447# as part of error handling. 448ignore() { 449 "$@" 450} 451 452# This wraps curl or wget. Try curl first, if not installed, 453# use wget instead. 454downloader() { 455 local _dld 456 local _ciphersuites 457 local _err 458 local _status 459 if check_cmd curl; then 460 _dld=curl 461 elif check_cmd wget; then 462 _dld=wget 463 else 464 _dld='curl or wget' # to be used in error message of need_cmd 465 fi 466 467 if [ "$1" = --check ]; then 468 need_cmd "$_dld" 469 elif [ "$_dld" = curl ]; then 470 get_ciphersuites_for_curl 471 _ciphersuites="$RETVAL" 472 if [ -n "$_ciphersuites" ]; then 473 _err=$(curl --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" 2>&1) 474 _status=$? 475 else 476 echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure" 477 if ! check_help_for "$3" curl --proto --tlsv1.2; then 478 echo "Warning: Not enforcing TLS v1.2, this is potentially less secure" 479 _err=$(curl --silent --show-error --fail --location "$1" --output "$2" 2>&1) 480 _status=$? 481 else 482 _err=$(curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1) 483 _status=$? 484 fi 485 fi 486 if [ -n "$_err" ]; then 487 echo "$_err" >&2 488 if echo "$_err" | grep -q 404$; then 489 err "installer for platform '$3' not found, this may be unsupported" 490 fi 491 fi 492 return $_status 493 elif [ "$_dld" = wget ]; then 494 get_ciphersuites_for_wget 495 _ciphersuites="$RETVAL" 496 if [ -n "$_ciphersuites" ]; then 497 _err=$(wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" 2>&1) 498 _status=$? 499 else 500 echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure" 501 if ! check_help_for "$3" wget --https-only --secure-protocol; then 502 echo "Warning: Not enforcing TLS v1.2, this is potentially less secure" 503 _err=$(wget "$1" -O "$2" 2>&1) 504 _status=$? 505 else 506 _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1) 507 _status=$? 508 fi 509 fi 510 if [ -n "$_err" ]; then 511 echo "$_err" >&2 512 if echo "$_err" | grep -q ' 404 Not Found$'; then 513 err "installer for platform '$3' not found, this may be unsupported" 514 fi 515 fi 516 return $_status 517 else 518 err "Unknown downloader" # should not reach here 519 fi 520} 521 522check_help_for() { 523 local _arch 524 local _cmd 525 local _arg 526 _arch="$1" 527 shift 528 _cmd="$1" 529 shift 530 531 local _category 532 if "$_cmd" --help | grep -q 'For all options use the manual or "--help all".'; then 533 _category="all" 534 else 535 _category="" 536 fi 537 538 case "$_arch" in 539 540 *darwin*) 541 if check_cmd sw_vers; then 542 case $(sw_vers -productVersion) in 543 10.*) 544 # If we're running on macOS, older than 10.13, then we always 545 # fail to find these options to force fallback 546 if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then 547 # Older than 10.13 548 echo "Warning: Detected macOS platform older than 10.13" 549 return 1 550 fi 551 ;; 552 11.*) 553 # We assume Big Sur will be OK for now 554 ;; 555 *) 556 # Unknown product version, warn and continue 557 echo "Warning: Detected unknown macOS major version: $(sw_vers -productVersion)" 558 echo "Warning TLS capabilities detection may fail" 559 ;; 560 esac 561 fi 562 ;; 563 564 esac 565 566 for _arg in "$@"; do 567 if ! "$_cmd" --help $_category | grep -q -- "$_arg"; then 568 return 1 569 fi 570 done 571 572 true # not strictly needed 573} 574 575# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites 576# if support by local tools is detected. Detection currently supports these curl backends: 577# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty. 578get_ciphersuites_for_curl() { 579 if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then 580 # user specified custom cipher suites, assume they know what they're doing 581 RETVAL="$RUSTUP_TLS_CIPHERSUITES" 582 return 583 fi 584 585 local _openssl_syntax="no" 586 local _gnutls_syntax="no" 587 local _backend_supported="yes" 588 if curl -V | grep -q ' OpenSSL/'; then 589 _openssl_syntax="yes" 590 elif curl -V | grep -iq ' LibreSSL/'; then 591 _openssl_syntax="yes" 592 elif curl -V | grep -iq ' BoringSSL/'; then 593 _openssl_syntax="yes" 594 elif curl -V | grep -iq ' GnuTLS/'; then 595 _gnutls_syntax="yes" 596 else 597 _backend_supported="no" 598 fi 599 600 local _args_supported="no" 601 if [ "$_backend_supported" = "yes" ]; then 602 # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. 603 if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then 604 _args_supported="yes" 605 fi 606 fi 607 608 local _cs="" 609 if [ "$_args_supported" = "yes" ]; then 610 if [ "$_openssl_syntax" = "yes" ]; then 611 _cs=$(get_strong_ciphersuites_for "openssl") 612 elif [ "$_gnutls_syntax" = "yes" ]; then 613 _cs=$(get_strong_ciphersuites_for "gnutls") 614 fi 615 fi 616 617 RETVAL="$_cs" 618} 619 620# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites 621# if support by local tools is detected. Detection currently supports these wget backends: 622# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty. 623get_ciphersuites_for_wget() { 624 if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then 625 # user specified custom cipher suites, assume they know what they're doing 626 RETVAL="$RUSTUP_TLS_CIPHERSUITES" 627 return 628 fi 629 630 local _cs="" 631 if wget -V | grep -q '\-DHAVE_LIBSSL'; then 632 # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. 633 if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then 634 _cs=$(get_strong_ciphersuites_for "openssl") 635 fi 636 elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then 637 # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. 638 if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then 639 _cs=$(get_strong_ciphersuites_for "gnutls") 640 fi 641 fi 642 643 RETVAL="$_cs" 644} 645 646# Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2 647# excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad 648# DH params often found on servers (see RFC 7919). Sequence matches or is 649# similar to Firefox 68 ESR with weak cipher suites disabled via about:config. 650# $1 must be openssl or gnutls. 651get_strong_ciphersuites_for() { 652 if [ "$1" = "openssl" ]; then 653 # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet. 654 echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384" 655 elif [ "$1" = "gnutls" ]; then 656 # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't. 657 # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order. 658 echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM" 659 fi 660} 661 662main "$@" || exit 1 663