changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > demo / tools/dep.sh

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