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