changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > infra / scripts/init.sh

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