Last active
September 30, 2024 18:32
-
-
Save kwekewk/5e02575c6cd78f64a6449f2d4929c2b7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# shellcheck shell=sh | |
# SPDX-License-Identifier: BSD-3-Clause | |
# Copyright (c) 2022, Unikraft GmbH and The KraftKit Authors. | |
# Licensed under the BSD-3-Clause License (the "License"). | |
# You may not use this file expect in compliance with the License. | |
# File taken and modified from: https://github.com/rust-lang/rustup | |
# Original credits go to the Rust project and its contributors. | |
# 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 'Installation does not work with this ksh93; please try bash!' >&2 | |
exit 1 | |
fi | |
# The following variables can be set to override the defaults: | |
PREFIX=${PREFIX:-/usr/local/bin} | |
INSTALL_TLS_CIPHERSUITES=${INSTALL_TLS_CIPHERSUITES:-} | |
INSTALL_CPUTYPE=${INSTALL_CPUTYPE:-} | |
INSTALL_SERVER=${INSTALL_SERVER:-https://get.kraftkit.sh} | |
INSTALL_TLS=${INSTALL_TLS:-y} | |
DEBUG=${DEBUG:-y} | |
NEED_TTY=${NEED_TTY:-y} | |
ASK_SUDO=${ASK_SUDO:-y} | |
# Commands as variables to make them easier to override | |
APK=${APK:-apk} | |
APT=${APT:-apt-get} | |
AWK=${AWK:-awk} | |
BREW=${BREW:-brew} | |
CAT=${CAT:-cat} | |
CURL=${CURL:-curl} | |
CUT=${CUT:-cut} | |
DIRNAME=${DIRNAME:-dirname} | |
GIT=${GIT:-git} | |
GREP=${GREP:-grep} | |
HEAD=${HEAD:-head} | |
INSTALL=${INSTALL:-install} | |
LDD=${LDD:-ldd} | |
MAKEPKG=${MAKEPKG:-makepkg} | |
MKDIR=${MKDIR:-mkdir} | |
MV=${MV:-mv} | |
PACMAN=${PACMAN:-pacman} | |
RM=${RM:-rm} | |
SUDO=${SUDO:-sudo} | |
SW_VERS=${SW_VERS:-sw_vers} | |
SYSCTL=${SYSCTL:-sysctl} | |
TAIL=${TAIL:-tail} | |
TAR=${TAR:-tar} | |
TR=${TR:-tr} | |
UNAME=${UNAME:-uname} | |
WGET=${WGET:-wget} | |
WHICH=${WHICH:-which} | |
YUM=${YUM:-yum} | |
set -u | |
trap cleanup_int HUP | |
trap cleanup_int INT | |
trap cleanup_int QUIT | |
trap cleanup_int ABRT | |
trap cleanup_int ALRM | |
trap cleanup_int TERM | |
# usage prints the usage message to stderr | |
usage() { | |
$CAT 1>&2 <<EOF | |
The KraftKit Installer. | |
Build and use highly customized and ultra-lightweight unikernels. | |
Documentation: https://kraftkit.sh/ | |
Issues & support: https://github.com/unikraft/kraftkit/issues | |
USAGE: | |
$0 [FLAGS] [OPTIONS] | |
FLAGS: | |
-d, --debug Enable debug output | |
-y Disable confirmation prompt | |
-h, --help Prints help information | |
-v, --version Prints version information | |
EOF | |
} | |
# Helper variables | |
_NO_ANS="^n$|^N$|^no$|^No$|^nO$" | |
_YES_ANS="^y$|^Y$|^yes$|^Yes$|^yEs$|^yeS$|^YEs$|^yES$|^YES$" | |
_NO_ANS_DEFAULT="^n$|^N$|^no$|^No$|^nO$|^\n$|^$" | |
_YES_ANS_DEFAULT="^y$|^Y$|^yes$|^Yes$|^yEs$|^yeS$|^YEs$|^yES$|^YES$|^\n$|^$" | |
_RETVAL="" | |
_CLEANUP_ARCHIVE="" | |
_CLEANUP_BINARY="" | |
_CLEANUP_VERSION="" | |
# say prints a message to stdout prefixed with space prefix. | |
# $1: message to print | |
# Returns: | |
say() { | |
printf ' %s\n' "$1" | |
} | |
# say_ok prints a message to stdout with a success prefix. | |
# $1: message to print | |
# Returns: | |
say_ok() { | |
printf '\e[38;5;10m[+]\033[0m %s\n' "$1" | |
} | |
# say_debug prints a message to stdout when DEBUG=y. | |
# $1: message to print | |
# Returns: | |
say_debug() { | |
if [ "$DEBUG" = "y" ]; then | |
printf '\e[38;5;4m[D]\033[0m %s\n' "$1" | |
fi | |
} | |
# say_warn prints a message to stdout with a warning prefix. | |
# $1: message to print | |
# Returns: | |
say_warn() { | |
printf '\e[38;5;11m[!]\033[0m %s\n' "$1" | |
} | |
# err prints a message to stderr prefixed with "kraftkit: " and exits | |
# $1: error to print | |
# Returns: | |
# Code: 1 | |
err() { | |
printf '\e[38;5;1m<!>\033[0m %s\n' "$1" >&2 | |
exit 1 | |
} | |
# need_cmd checks if a command is available and exits if it is not. | |
# $1: command to check | |
# Returns: | |
need_cmd() { | |
say_debug "Checking for command $1" | |
if ! check_cmd "$1"; then | |
err "need '$1' (command not found)" | |
fi | |
} | |
# check_cmd checks if the path to a command exists. | |
# $1: command to check | |
# Returns: | |
# Code: 1 if the command exists, 0 otherwise | |
check_cmd() { | |
command -v "$1" > /dev/null 2>&1 | |
} | |
# assert_nz checks if a variable is not empty and exits if it is. | |
# $1: variable to check | |
# $2: error message to print | |
assert_nz() { | |
if [ -z "$1" ]; then err "assert_nz $2"; fi | |
} | |
# ensure runs a command that should never fail. If the command fails execution | |
# will immediately terminate with an error showing the failing command. | |
# $@: command to run | |
# Returns: | |
ensure() { | |
if ! "$@"; then err "command failed: $*"; fi | |
} | |
# cleanup_int is the interrupt handler for the installer. | |
# Returns: | |
# Code: 1 | |
cleanup_int() { | |
say "" | |
cleanup | |
err "user interrupt: exiting..." | |
} | |
# cleanup removes any temporary files created during installation. | |
# Returns: | |
# Code: 1 if the command exists, 0 otherwise | |
cleanup() { | |
if [ -n "$_CLEANUP_ARCHIVE" ] || \ | |
[ -n "$_CLEANUP_BINARY" ] || \ | |
[ -n "$_CLEANUP_VERSION" ]; then | |
do_cmd "$RM -f $_CLEANUP_BINARY $_CLEANUP_ARCHIVE $_CLEANUP_VERSION" | |
fi | |
} | |
# get_user_response asks the user a question and returns the answer in '_RETVAL' | |
# $1: question | |
# $2: default answer | |
# Returns: | |
# _RETVAL: answer | |
get_user_response() { | |
_gur_question="$1" | |
_gur_default="$2" | |
_gur_read_answer="" | |
# Ask the question | |
printf "[?] %s" "$_gur_question" | |
# Check if we have a tty and read from it if we do | |
if [ "$NEED_TTY" = "y" ]; then | |
read -r _gur_read_answer < /dev/tty | |
fi | |
# If we the answer is empty, or we don't have a tty, use the default | |
if [ -z "$_gur_read_answer" ]; then | |
_gur_read_answer="$_gur_default" | |
fi | |
# Return the answer | |
_RETVAL="$_gur_read_answer" | |
} | |
# prompt_yes_no asks the user a question which must be answered with yes or no. | |
# Returns a boolean value. | |
# $1: question | |
# $2: default answer | |
# Returns: | |
# _RETVAL: true for "yes", false for "no" | |
prompt_yes_no() { | |
while true; | |
do | |
get_user_response "$1" "$2" | |
_answer="$_RETVAL" | |
if printf "%s" "$_answer" | "$GREP" -q -E "$_NO_ANS"; then | |
return 1 | |
elif printf "%s" "$_answer" | "$GREP" -q -E "$_YES_ANS"; then | |
return 0 | |
else | |
printf "\e[38;5;1m<!>\033[0m Invalid input. Please try again or exit with Ctrl+C\n" | |
fi | |
done | |
} | |
# do_cmd runs a command and asks the user if they want to retry with root | |
# if it fails | |
# $@: command | |
# Returns: | |
# _cmd_retval: return value of the command | |
# Code: _cmd_retval: return value of the command on fail | |
do_cmd() { | |
# Print the command out to stderr for users to see | |
echo "+" "$@" > /dev/stderr | |
# Run the command if the command exists | |
if ! sh -c "$@"; then | |
# Take the return value of the command | |
_cmd_retval="$?" | |
# Inform the user that the command failed | |
say_warn "command failed: $*" | |
# Retry with root if the user wants to, otherwise exit | |
# with the return value of the command | |
if [ "$ASK_SUDO" = "n" ] || prompt_yes_no \ | |
"Do you want to retry with root from now on? [y/N]: " "n"; then | |
# Check if we have a tty and run with it if we do, | |
# otherwise exit with an error | |
# This is because sudo requires a tty to input the password | |
if [ "$NEED_TTY" = "y" ]; then | |
# shellcheck disable=SC2002 | |
$SUDO sh -c "$@" < /dev/tty | |
ASK_SUDO="n" | |
else | |
err "Cannot retry with root without a tty." | |
fi | |
else | |
exit "$_cmd_retval" | |
fi | |
fi | |
} | |
# check_proc checks if /proc is mounted by looking for /proc/self/exe for Linux | |
# Returns: | |
check_proc() { | |
if ! test -L /proc/self/exe ; then | |
err "Unable to find /proc/self/exe. Is /proc mounted?"\ | |
"Installation cannot proceed without /proc." | |
fi | |
} | |
# check_help_for prints the help for a command with a specific set of options | |
# $1: architecture/command to check | |
# $@: options to check | |
# Returns: | |
check_help_for() { | |
_chf_cmd="" | |
_chf_arg="" | |
_chf_arch="$1" | |
shift | |
_chf_cmd="$1" | |
shift | |
_chf_category="" | |
_chf_cmd_help_check='For all options use the manual or "--help all".' | |
if "$_chf_cmd" --help | "$GREP" -q "$_chf_cmd_help_check"; then | |
_chf_category="all" | |
else | |
_chf_category="" | |
fi | |
case "$_chf_arch" in | |
*darwin*) | |
if check_cmd "$SW_VERS"; then | |
_chf_ver_arg="-productVersion" | |
case $("$SW_VERS" "$_chf_ver_arg") in | |
10.*) | |
# If we're running on macOS, older than 10.13, then we | |
# always fail to find these options to force fallback | |
_chf_ck_arg="$("$SW_VERS" "$_chf_ver_arg" | "$CUT" -d. -f2)" | |
if [ "$_chf_ck_arg" -lt 13 ]; then | |
# Older than 10.13 | |
say_warn "Detected macOS platform older than 10.13" | |
return 1 | |
fi | |
;; | |
11.*) | |
# We assume Big Sur will be OK for now | |
;; | |
*) | |
# Unknown product version, warn and continue | |
say_warn "Detected unknown macOS major version:"\ | |
"$("$SW_VERS" "$_chf_ver_arg")" | |
say_warn "TLS capabilities detection may fail." | |
;; | |
esac | |
fi | |
;; | |
esac | |
for _chf_arg in "$@"; do | |
"$_chf_cmd" --help "$_chf_category" | "$GREP" -q -- "$_chf_arg" | |
_chf_cmd_res="$?" | |
if [ "$_chf_cmd_res" != "0" ]; then | |
return 1 | |
fi | |
done | |
true # not strictly needed | |
} | |
# Check if curl supports the --retry flag, then pass it to the curl invocation. | |
# Returns: | |
# _RETVAL: empty string if not supported, "--retry 3" if supported | |
check_curl_for_retry_support() { | |
_ccr_retry_supported="" | |
# "unspecified" is for arch, allows for possibility old OS | |
# using macports, homebrew, etc. | |
if check_help_for "notspecified" "curl" "--retry"; then | |
_ccr_retry_supported="--retry 3" | |
fi | |
_RETVAL="$_ccr_retry_supported" | |
} | |
# check_os_release checks if the given OS is in the ID_LIKE or ID field of | |
# /etc/os-release. | |
# Returns: | |
# Code: 0 if the OS is found, 1 otherwise | |
check_os_release() { | |
_cor_os="$1" | |
_cor_ck1="" | |
_cor_ck2="" | |
# False positive - it does not interpret it as awk code because of the macro | |
# shellcheck disable=SC2016 | |
"$AWK" -F= '/^ID_LIKE/{print $2}' /etc/os-release | "$GREP" -q "$_cor_os" | |
_cor_ck1=$? | |
# False positive - it does not interpret it as awk code because of the macro | |
# shellcheck disable=SC2016 | |
"$AWK" -F= '/^ID/{print $2}' /etc/os-release | "$GREP" -q "$_cor_os" | |
_cor_ck2=$? | |
say_debug "ID_LIKE: $_cor_ck1, ID: $_cor_ck2" | |
[ "$_cor_ck1" = "0" ] || [ "$_cor_ck2" = "0" ] | |
return $? | |
} | |
# is_host_amd64_elf returns true if the current platform is amd64. | |
# Returns 0 if true, 1 if false. | |
is_host_amd64_elf() { | |
need_cmd "$HEAD" | |
need_cmd "$TAIL" | |
# ELF e_machine detection without dependencies beyond coreutils. | |
# Two-byte field at offset 0x12 indicates the CPU, | |
# but we're interested in it being 0x3E to indicate amd64, or not that. | |
_iha_current_exe_machine="" | |
_iha_current_exe_machine=$("$HEAD" -c 19 /proc/self/exe | "$TAIL" -c 1) | |
[ "$_iha_current_exe_machine" = "$(printf '\076')" ] | |
} | |
# get_bitness returns the bitness of the current platform. | |
# Echoes 32 or 64. | |
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. | |
_gbt_current_exe_head="" | |
_gbt_current_exe_head=$("$HEAD" -c 5 /proc/self/exe ) | |
if [ "$_gbt_current_exe_head" = "$(printf '\177ELF\001')" ]; then | |
echo 32 | |
elif [ "$_gbt_current_exe_head" = "$(printf '\177ELF\002')" ]; then | |
echo 64 | |
else | |
err "fatal: unknown platform bitness" | |
fi | |
} | |
# get_endianness returns the endianness of the current platform. | |
# Echoes the cputype with the suffix appended. | |
get_endianness() { | |
_ged_cputype="$1" | |
_ged_suffix_eb="$2" | |
_ged_suffix_el="$3" | |
# detect endianness without od/hexdump, like get_bitness() does. | |
need_cmd "$HEAD" | |
need_cmd "$TAIL" | |
_ged_current_exe_endianness="" | |
_ged_current_exe_endianness="$("$HEAD" -c 6 /proc/self/exe | "$TAIL" -c 1)" | |
if [ "$_ged_current_exe_endianness" = "$(printf '\001')" ]; then | |
echo "${_ged_cputype}${_ged_suffix_el}" | |
elif [ "$_ged_current_exe_endianness" = "$(printf '\002')" ]; then | |
echo "${_ged_cputype}${_ged_suffix_eb}" | |
else | |
err "Unknown platform endianness" | |
fi | |
} | |
# get_linux_types returns the ostype and clibtype if the platform is based | |
# on Linux. | |
# $1: the ostype to check | |
# $2: the clibtype to check | |
# Returns: | |
# _OSTYPE: the ostype to use | |
# _CLIBTYPE: the clibtype to use | |
get_linux_types() { | |
_glt_ostype="$1" | |
_glt_clibtype="$2" | |
if [ "$_glt_ostype" = Linux ]; then | |
# Check if the OS is Android | |
if [ "$($UNAME -o)" = Android ]; then | |
_OSTYPE="Android" | |
fi | |
# Check if the OS is Alpine (based on musl) | |
if $LDD --version 2>&1 | "$GREP" -q 'musl'; then | |
_CLIBTYPE="musl" | |
fi | |
fi | |
} | |
# get_darwin_types returns the ostype and clibtype if the platform is based | |
# on Darwin. | |
# $1: the ostype to check | |
# $2: the cputype to check | |
# Returns: | |
# _OSTYPE: the ostype to use | |
# _CPUTYPE: the cputype to use | |
get_darwin_types() { | |
_gdt_ostype="$1" | |
_gdt_cputype="$2" | |
if [ "$_gdt_ostype" = Darwin ] && [ "$_gdt_cputype" = i386 ]; then | |
# Darwin `uname -m` lies so we need to do further checks | |
if $SYSCTL hw.optional.x86_64 | "$GREP" -q ': 1'; then | |
_CPUTYPE="x86_64" | |
fi | |
fi | |
} | |
# get_sunos_types returns the ostype and clibtype if the platform is based | |
# on SunOS. | |
# $1: the ostype to check | |
# $2: the cputype to check | |
# Returns: | |
# _OSTYPE: the ostype to use | |
# _CPUTYPE: the cputype to use | |
get_sunos_types() { | |
_gst_ostype="$1" | |
_gst_cputype="$2" | |
if [ "$_gst_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 [ "$_gst_cputype" = i86pc ]; then | |
_CPUTYPE="$(isainfo -n)" | |
fi | |
fi | |
} | |
# resolve_os_type returns the ostype and clibtype for the current platform. | |
# $1: the ostype to check | |
# $2: the clibtype to check | |
# Returns: | |
# _OSTYPE: the resolved ostype | |
# _BITNESS: the bitness of the current platform | |
resolve_os_type() { | |
_rot_ostype="$1" | |
_rot_clibtype="$2" | |
case "$_rot_ostype" in | |
Android) | |
_OSTYPE=linux-android | |
;; | |
Linux) | |
check_proc | |
_OSTYPE=unknown-linux-$_rot_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* | Windows_NT) | |
_OSTYPE=pc-windows-gnu | |
;; | |
*) | |
err "Unrecognized OS type: $_rot_ostype" | |
;; | |
esac | |
} | |
# resolve_cpu_type returns the cputype for the current platform. | |
# $1: the cputype to check | |
# $2: the bitness of the current platform | |
# Returns: | |
# _CPUTYPE: the resolved cputype | |
# _OSTYPE: the resolved ostype | |
resolve_cpu_type() { | |
_rct_cputype="$1" | |
_rct_bitness="$2" | |
case "$_rct_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 [ "$_rct_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: $_rct_cputype" | |
esac | |
} | |
# resolve_cpu_unknown resolves the cputype for the current platform if it is | |
# detected as unknown and 32 bit. | |
# $1: the bitness of the current platform | |
# Returns: | |
# _CPUTYPE: the resolved cputype | |
# _OSTYPE: the resolved ostype | |
resolve_cpu_unknown() { | |
_rcu_bitness="$1" | |
_rcu_unknown="unknown-linux-gnu" | |
if [ "${_OSTYPE}" = "$_rcu_unknown" ] && [ "${_rcu_bitness}" = "32" ]; then | |
case $_CPUTYPE in | |
x86_64) | |
if [ -n "${INSTALL_CPUTYPE:-}" ]; then | |
_CPUTYPE="$INSTALL_CPUTYPE" | |
else { | |
# 32-bit executable for amd64 = x32 | |
if is_host_amd64_elf; then { | |
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 | |
} | |
# resolve_cpu_unknown_arm resolves the cputype for the current platform if it is | |
# detected as unknown and arm. | |
# $1: the ostype to check | |
# $2: the cputype to check | |
# Returns: | |
# _OSTYPE: the resolved cputype | |
resolve_cpu_unknown_arm() { | |
_rca_ostype="$1" | |
_rca_cputype="$2" | |
_rca_unk_lin="unknown-linux-gnueabihf" | |
if [ "$_rca_ostype" = "$_rca_unk_lin" ] && [ "$_rca_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 | |
} | |
# get_architecture returns the architecture of the current platform. | |
# Returns: | |
# _RETVAL: the os-cpu of the current platform. | |
get_architecture() { | |
# Run basic checks to get the os and architecture | |
_OSTYPE="$($UNAME -s)" | |
_CPUTYPE="$($UNAME -m)" | |
_CLIBTYPE="gnu" | |
_BITNESS="" | |
_ARCH="" | |
# Check if the OS is Linux based | |
get_linux_types "$_OSTYPE" "$_CLIBTYPE" | |
# Check if the OS is Mac based | |
get_darwin_types "$_OSTYPE" "$_CPUTYPE" | |
# Check if the OS is Solaris or illumos based | |
get_sunos_types "$_OSTYPE" "$_CPUTYPE" | |
say_debug "Detected OS: $_OSTYPE, CPU: $_CPUTYPE, LIBC: $_CLIBTYPE" | |
# Check the OS variable and construct the first part of the return string | |
resolve_os_type "$_OSTYPE" "$_CLIBTYPE" | |
# Check the CPU variable and construct the second part of the return string | |
resolve_cpu_type "$_CPUTYPE" "$_BITNESS" | |
# Check the CPU/OS variables if 64-bit linux with 32-bit userland | |
resolve_cpu_unknown "$_BITNESS" | |
# Check the CPU/OS variables if unknown arm | |
resolve_cpu_unknown_arm "$_OSTYPE" "$_CPUTYPE" | |
_ARCH="${_CPUTYPE}-${_OSTYPE}" | |
_RETVAL="$_ARCH" | |
} | |
# Return cipher suite string specified by user, otherwise return strong | |
# TLS 1.2-1.3 cipher suites if support by local tools is detected. Detection | |
# currently supports these curl backends: GnuTLS and OpenSSL (possibly also | |
# LibreSSL and BoringSSL). | |
# Returns: | |
# _RETVAL: cipher suite string. Can be empty. | |
get_ciphersuites_for_curl() { | |
if [ -n "${INSTALL_TLS_CIPHERSUITES-}" ]; then | |
# user specified custom cipher suites | |
# assume they know what they're doing | |
say_debug "Using user specified cipher suites" | |
_RETVAL="$INSTALL_TLS_CIPHERSUITES" | |
return | |
fi | |
_gcf_openssl_syntax="no" | |
_gcf_gnutls_syntax="no" | |
_gcf_backend_supported="yes" | |
if "$CURL" -V | "$GREP" -q ' OpenSSL/'; then | |
_gcf_openssl_syntax="yes" | |
elif "$CURL" -V | "$GREP" -iq ' LibreSSL/'; then | |
_gcf_openssl_syntax="yes" | |
elif "$CURL" -V | "$GREP" -iq ' BoringSSL/'; then | |
_gcf_openssl_syntax="yes" | |
elif "$CURL" -V | "$GREP" -iq ' GnuTLS/'; then | |
_gcf_gnutls_syntax="yes" | |
else | |
_gcf_backend_supported="no" | |
fi | |
_gcf_args_supported="no" | |
if [ "$_gcf_backend_supported" = "yes" ]; then | |
if check_help_for "notspecified" "$CURL" "--tlsv1.2" "--ciphers" \ | |
"--proto"; then | |
_gcf_args_supported="yes" | |
fi | |
fi | |
_gcf_cs="" | |
if [ "$_gcf_args_supported" = "yes" ]; then | |
if [ "$_gcf_openssl_syntax" = "yes" ]; then | |
say_debug "Using OpenSSL syntax for ciphersuites" | |
_gcf_cs=$(get_strong_ciphersuites_for "openssl") | |
elif [ "$_gcf_gnutls_syntax" = "yes" ]; then | |
say_debug "Using GnuTLS syntax for ciphersuites" | |
_gcf_cs=$(get_strong_ciphersuites_for "gnutls") | |
fi | |
fi | |
_RETVAL="$_gcf_cs" | |
} | |
# Return cipher suite string specified by user, otherwise return strong | |
# TLS 1.2-1.3 cipher suites if support by local tools is detected. Detection | |
# currently supports these wget backends: GnuTLS and OpenSSL (possibly also | |
# LibreSSL and BoringSSL). | |
# Returns: | |
# _RETVAL: cipher suite string. Can be empty. | |
get_ciphersuites_for_wget() { | |
if [ -n "${INSTALL_TLS_CIPHERSUITES-}" ]; then | |
# User specified custom cipher suites, assume they know what | |
# they're doing | |
say_debug "Using user specified cipher suites" | |
_RETVAL="$INSTALL_TLS_CIPHERSUITES" | |
return | |
fi | |
_gcw_cs="" | |
if "$WGET" -V | "$GREP" -q '\-DHAVE_LIBSSL'; then | |
# "unspecified" is for arch, allows for possible old OSes | |
if check_help_for "notspecified" "$WGET" "TLSv1_2" "--ciphers" \ | |
"--https-only" "--secure-protocol"; then | |
say_debug "Using strong cipher suites for wget from OpenSSL" | |
_gcw_cs=$(get_strong_ciphersuites_for "openssl") | |
fi | |
elif "$WGET" -V | "$GREP" -q '\-DHAVE_LIBGNUTLS'; then | |
# "unspecified" is for arch, allows for possible old OSes | |
if check_help_for "notspecified" "$WGET" "TLSv1_2" "--ciphers" \ | |
"--https-only" "--secure-protocol"; then | |
say_debug "Using strong cipher suites for wget from GnuTLS" | |
_gcw_cs=$(get_strong_ciphersuites_for "gnutls") | |
fi | |
fi | |
_RETVAL="$_gcw_cs" | |
} | |
# Strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2 | |
# excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad | |
# DH params often found on servers (see RFC 7919). Sequence matches or is | |
# similar to Firefox 68 ESR with weak cipher suites disabled via about:config. | |
# $1: "openssl" or "gnutls" | |
# Returns: | |
# echo: cipher suite string | |
get_strong_ciphersuites_for() { | |
_gsc_cs="$1" | |
if [ "$_gsc_cs" = "openssl" ]; then | |
# OpenSSL is forgiving of unknown values, no problems with TLS 1.3 | |
# values on versions that don't support it yet. | |
printf "%s%s%s%s%s\n" \ | |
"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" | |
elif [ "$_gsc_cs" = "gnutls" ]; then | |
# GnuTLS isn't forgiving of unknown values, so this may require a | |
# GnuTLS version that supports TLS 1.3 even if wget doesn't. | |
# Begin with SECURE128 (and higher) then remove/add to build cipher | |
# suites. Produces same 9 cipher suites as OpenSSL but in slightly | |
# different order. | |
printf "%s%s%s%s\n" \ | |
"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" | |
fi | |
} | |
# download_using_curl downloads the given file using curl | |
# $1: the url to download | |
# $2: the archive to download to | |
# $3: the architecture of the download | |
# Returns: | |
# _RETVAL: return code of curl | |
# Code: 0 on success, 1 on error | |
download_using_curl() { | |
_duc_url="$1" | |
_duc_archive="$2" | |
_duc_arch="$3" | |
_duc_retry="" | |
_duc_ciphersuites="" | |
_duc_err="" | |
check_curl_for_retry_support | |
_duc_retry="$_RETVAL" | |
if [ "$INSTALL_TLS" = "y" ]; then | |
get_ciphersuites_for_curl | |
_duc_ciphersuites="$_RETVAL" | |
if [ -n "$_duc_ciphersuites" ]; then | |
say_debug "Enforcing strong cipher suites for TLS" | |
# shellcheck disable=SC2086 | |
_duc_err=$("$CURL" $_duc_retry --proto '=https' --tlsv1.2 \ | |
--ciphers "$_duc_ciphersuites" --silent --show-error --fail \ | |
--location "$_duc_url" --output "$_duc_archive" 2>&1) | |
_RETVAL=$? | |
else | |
say_warn "Not enforcing strong cipher suites for TLS" | |
if ! check_help_for "$_duc_arch" "$CURL" --proto --tlsv1.2; then | |
say_warn "Not enforcing TLS v1.2; less secure" | |
# shellcheck disable=SC2086 | |
_duc_err=$("$CURL" $_duc_retry --silent --show-error \ | |
--fail --location "$_duc_url" --output "$_duc_archive" 2>&1) | |
_RETVAL=$? | |
else | |
# shellcheck disable=SC2086 | |
_duc_err=$("$CURL" $_duc_retry --proto '=https' --tlsv1.2 \ | |
--silent --show-error --fail --location "$_duc_url" \ | |
--output "$_duc_archive" 2>&1) | |
_RETVAL=$? | |
fi | |
fi | |
else | |
say_warn "Not enforcing cipher suites for TLS; less secure" | |
# shellcheck disable=SC2086 | |
_duc_err=$("$CURL" $_duc_retry --silent \ | |
--show-error --fail --location "$_duc_url" \ | |
--output "$_duc_archive" 2>&1) | |
_RETVAL=$? | |
fi | |
if [ -n "$_duc_err" ]; then | |
echo "$_duc_err" >&2 | |
if echo "$_duc_err" | $GREP -q 404$; then | |
_duc_msg="" | |
_duc_msg=$(printf "%s%s" \ | |
"installer for platform '$_duc_arch' not found, " \ | |
"this may be unsupported" \ | |
) | |
err "$_duc_msg" | |
fi | |
fi | |
} | |
# download_using_wget downloads the given file using wget | |
# $1: the url to download | |
# $2: the archive to download to | |
# $3: the architecture of the download | |
# Returns: | |
# _RETVAL: return code of wget | |
# Code: 0 on success, 1 on error | |
download_using_wget() { | |
_duw_url="$1" | |
_duw_archive="$2" | |
_duw_arch="$3" | |
_duw_err="" | |
_duw_ciphersuites="" | |
_duw_chk_busybox="$("$WGET" -V 2>&1|"$HEAD" -2|"$TAIL" -1|"$CUT" -f1 -d" ")" | |
if [ "$INSTALL_TLS" = "y" ]; then | |
if [ "$_duw_chk_busybox" = "BusyBox" ]; then | |
say_warn "Using the BusyBox version of $WGET."\ | |
"Not enforcing strong cipher suites for TLS or TLS v1.2" | |
_duw_err=$("$WGET" "$_duw_url" -O "$_duw_archive" 2>&1) | |
_RETVAL=$? | |
else | |
get_ciphersuites_for_wget | |
_duw_ciphersuites="$_RETVAL" | |
if [ -n "$_duw_ciphersuites" ]; then | |
_duw_err=$("$WGET" --https-only \ | |
--secure-protocol=TLSv1_2 \ | |
--ciphers "$_duw_ciphersuites" "$_duw_url" \ | |
-O "$_duw_archive" 2>&1) | |
_RETVAL=$? | |
else | |
say_warn "Not enforcing strong suites for TLS; less secure" | |
_help_chk=$(check_help_for "$_duw_arch" "$WGET" \ | |
--https-only --secure-protocol) | |
if ! "$_help_chk"; then | |
say_warn "Not enforcing TLS v1.2, this is less secure" | |
_duw_err=$("$WGET" "$_duw_url" -O "$_duw_archive" 2>&1) | |
_RETVAL=$? | |
else | |
_duw_err=$("$WGET" --https-only \ | |
--secure-protocol=TLSv1_2 "$_duw_url" \ | |
-O "$_duw_archive" 2>&1) | |
_RETVAL=$? | |
fi | |
fi | |
fi | |
else | |
warn "Not enforcing cipher suites for TLS; less secure" | |
_duw_err=$("$WGET" "$_duw_url" -O "$_duw_archive" 2>&1) | |
_RETVAL=$? | |
fi | |
if [ -n "$_duw_err" ]; then | |
echo "$_duw_err" >&2 | |
if echo "$_duw_err" | $GREP -q ' 404 Not Found$'; then | |
_duc_msg="" | |
_duc_msg=$(printf "%s%s" \ | |
"Installer for platform '$_duw_arch' not found, " \ | |
"this may be unsupported" \ | |
) | |
err "$_duc_msg" | |
fi | |
fi | |
} | |
# downloader wraps curl or wget. Try curl first, if not installed, | |
# use wget instead. | |
# $1: the url to download | |
# $2: the archive to download to | |
# $3: the architecture of the download | |
# Returns: | |
# _RETVAL: return code of curl or wget | |
# Code: 0 on success, 1 on error | |
downloader() { | |
_dow_url="$1" | |
_dow_archive="$2" | |
_dow_arch="$3" | |
_dow_dld="" | |
if check_cmd "$CURL"; then | |
_dow_dld="$CURL" | |
elif check_cmd "$WGET"; then | |
_dow_dld="$WGET" | |
else | |
# To be used in error message of need_cmd | |
_dow_dld="$CURL or $WGET" | |
fi | |
if [ "$_dow_url" = --check ]; then | |
need_cmd "$_dow_dld" | |
elif [ "$_dow_dld" = "$CURL" ]; then | |
say_debug "Using $CURL to download" | |
download_using_curl "$_dow_url" "$_dow_archive" "$_dow_arch" | |
return $_RETVAL | |
elif [ "$_dow_dld" = "$WGET" ]; then | |
say_debug "Using $WGET to download" | |
download_using_wget "$_dow_url" "$_dow_archive" "$_dow_arch" | |
return $_RETVAL | |
else | |
# Should not reach this | |
err "Unknown downloader." | |
fi | |
} | |
# install_linux_gnu installs the kraftkit package for standard linux | |
# distributions that use glibc. | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_linux_gnu() { | |
need_cmd "$AWK" | |
need_cmd "$GREP" | |
if check_os_release "rhel" || check_os_release "fedora"; then | |
need_cmd "$YUM" | |
_ilg_rpm_path=$(printf "%s%s%s%s%s" \ | |
"[kraftkit]\n" \ | |
"name=Kraftkit Repo\n" \ | |
"baseurl=https://rpm.pkg.kraftkit.sh\n" \ | |
"enabled=1\n" \ | |
"gpgcheck=0\n" \ | |
) | |
say_ok "Adding kraftkit package for RHEL/Fedora" | |
_ilg_yum_cmd=$(printf "%b%s" \ | |
"echo '${_ilg_rpm_path}'" \ | |
"| tee /etc/yum.repos.d/kraftkit.repo" \ | |
) | |
do_cmd "$_ilg_yum_cmd" | |
do_cmd "$YUM makecache" | |
do_cmd "$YUM install -y kraftkit" | |
elif check_os_release "debian"; then | |
need_cmd "$APT" | |
_ilg_deb_path="deb [trusted=yes] https://deb.pkg.kraftkit.sh /" | |
_ilg_deb_cmd=$(printf "%s%s" \ | |
"echo '${_ilg_deb_path}' | " \ | |
"tee /etc/apt/sources.list.d/kraftkit.list" \ | |
) | |
_idd_recommended="" | |
if prompt_yes_no "Install recommended dependencies? [Y/n]: " "y"; then | |
_idd_recommended="--install-recommends" | |
else | |
_idd_recommended="--no-install-recommends" | |
fi | |
do_cmd "$_ilg_deb_cmd" | |
do_cmd "$APT --allow-unauthenticated update" | |
do_cmd "$APT install -y $_idd_recommended kraftkit" | |
elif check_os_release "arch"; then | |
need_cmd "$GIT" | |
need_cmd "$MAKEPKG" | |
need_cmd "$RM" | |
do_cmd "$GIT clone https://aur.archlinux.org/kraftkit-bin.git /tmp/kraftkit-bin" | |
cd /tmp/kraftkit-bin || exit | |
do_cmd "$MAKEPKG -si" | |
cd - 1> /dev/null || exit | |
$RM -rf /tmp/kraftkit-bin | |
else | |
_ilg_msg=$(printf "%s%s%s" \ | |
"Unsupported Linux distribution. " \ | |
"Try downloading the tar.gz file from " \ | |
"https://github.com/unikraft/kraftkit or switch to manual mode" \ | |
) | |
err "$_ilg_msg" | |
fi | |
} | |
# install_linux_musl installs the kraftkit package for non-standard linux | |
# distributions that use musl (musl). | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_linux_musl() { | |
need_cmd "$AWK" | |
need_cmd "$GREP" | |
if check_os_release "alpine"; then | |
need_cmd "$APK" | |
_ilm_cmd=$(printf "%s%s" \ | |
"$APK add --no-cache --repository " \ | |
"https://apk.pkg.kraftkit.sh kraftkit" \ | |
) | |
do_cmd "$_ilm_cmd" | |
else | |
_ilm_msg=$(printf "error: %s%s%s" \ | |
"Unsupported Linux distribution. " \ | |
"Try downloading the tar.gz file from " \ | |
"https://github.com/unikraft/kraftkit or switch to manual mode" \ | |
) | |
err "$_ilm_msg" | |
fi | |
} | |
# install_darwin installs the kraftkit package for MacOS distributions. | |
# $1: the architecture of the download | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_darwin() { | |
_ind_arch="$1" | |
need_cmd "$BREW" | |
do_cmd "$BREW install unikraft/cli/kraftkit" | |
} | |
# install_windows installs the kraftkit package for windows distributions. | |
# Currently not implemented. | |
# $1: the architecture of the download | |
# Returns: | |
# Code: 1 | |
install_windows() { | |
_inw_arch="$1" | |
_inw_url="https://github.com/unikraft/kraftkit/issues/267" | |
_inw_ext=".msi" | |
err "Windows architecture unsupported: $_inw_arch."\ | |
"You can contribute at $_inw_url" | |
} | |
# install_linux_manual installs the kraftkit package for other Linux | |
# distributions using the GitHub binaries. | |
# $1: the architecture of the download | |
# Returns: | |
# Code: 1 | |
install_linux_manual() { | |
need_cmd "$CAT" | |
need_cmd "$GREP" | |
need_cmd "$MKDIR" | |
need_cmd "$TAR" | |
need_cmd "$INSTALL" | |
need_cmd "$RM" | |
need_cmd "$WHICH" | |
need_cmd "$DIRNAME" | |
_ill_arch="$1" | |
_ill_version_url="$INSTALL_SERVER/latest.txt" | |
_ill_version_file="latest.txt" | |
downloader "$_ill_version_url" "$_ill_version_file" "$_ill_arch" | |
say_debug "Latest KraftKit version is $("$CAT" $_ill_version_file)" | |
_ill_url=$(printf "%s%s%s%s" \ | |
"https://github.com/unikraft/kraftkit" \ | |
"/releases/latest/download/kraft_" \ | |
"$("$CAT" $_ill_version_file)" \ | |
"_linux_amd64.tar.gz" | |
) | |
_CLEANUP_VERSION="$_ill_version_file" | |
# Check if binary already exists | |
if check_cmd "kraft"; then | |
_ill_kraft_path="$($WHICH kraft)" | |
_ill_prompt_msg=$(printf "%s%s%s" \ | |
"The kraft binary already installed at \"$_ill_kraft_path\", " \ | |
"overwrite with the latest version ($($CAT $_ill_version_file))? " \ | |
"[y/N]: " \ | |
) | |
if prompt_yes_no "$_ill_prompt_msg" "n"; then | |
PREFIX="$($DIRNAME "$_ill_kraft_path")" | |
else | |
say_ok "Already installed." | |
exit 0 | |
fi | |
else | |
if prompt_yes_no "Change the install prefix? [$PREFIX] [y/N]: " "n"; then | |
get_user_response "What should the prefix be? [$PREFIX]: " "$PREFIX" | |
PREFIX="$_RETVAL" | |
fi | |
if [ ! -d "$PREFIX" ]; then | |
if prompt_yes_no "Prefix does not exist, create? [y/N]: " "n"; then | |
do_cmd "$MKDIR -p $PREFIX" | |
else | |
err "Prefix does not exist." | |
fi | |
fi | |
fi | |
_ill_binary="kraft" | |
_ill_archive="kraft.tar.gz" | |
downloader "$_ill_url" "$_ill_archive" "$_ill_arch" | |
_CLEANUP_ARCHIVE="$_ill_archive" | |
do_cmd "$TAR -xzf $_ill_archive" | |
_CLEANUP_BINARY="$_ill_binary" | |
do_cmd "$INSTALL $_ill_binary $PREFIX" | |
cleanup | |
_CLEANUP_ARCHIVE="" | |
_CLEANUP_BINARY="" | |
_CLEANUP_VERSION="" | |
} | |
# install_darwin_manual installs the kraftkit package for other Darwin | |
# distributions using the GitHub binaries. | |
# $1: the architecture of the download | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_darwin_manual() { | |
need_cmd "$CAT" | |
need_cmd "$GREP" | |
need_cmd "$MKDIR" | |
need_cmd "$TAR" | |
need_cmd "$INSTALL" | |
need_cmd "$RM" | |
need_cmd "$WHICH" | |
need_cmd "$DIRNAME" | |
_idr_arch="$1" | |
_idr_arch_parsed="amd64" | |
# If arch contains x86_64, then translate to amd64 | |
if printf "%s" "$_idr_arch" | "$GREP" -q -E "x86_64"; then | |
_idr_arch_parsed="amd64" | |
elif printf "%s" "$_idr_arch" | "$GREP" -q -E "aarch64"; then | |
_idr_arch_parsed="arm64" | |
else | |
err "Unsupported architecture: $_idr_arch" | |
fi | |
_idr_version_url="$INSTALL_SERVER/latest.txt" | |
_idr_version_file="latest.txt" | |
downloader "$_idr_version_url" "$_idr_version_file" "$_idr_arch" | |
say_debug "Latest KraftKit version is $("$CAT" $_idr_version_file)" | |
_idr_url=$(printf "%s%s%s%s%s" \ | |
"https://github.com/unikraft/kraftkit" \ | |
"/releases/latest/download/kraft_" \ | |
"$("$CAT" $_idr_version_file)" \ | |
"_darwin_$_idr_arch_parsed" \ | |
".tar.gz" | |
) | |
_CLEANUP_VERSION="$_idr_version_file" | |
# Check if binary already exists | |
if check_cmd "kraft"; then | |
_idr_kraft_path="$($WHICH kraft)" | |
_idr_prompt_msg=$(printf "%s%s%s" \ | |
"The kraft binary already installed at \"$_idr_kraft_path\", " \ | |
"overwrite with the latest version ($($CAT $_idr_version_file))? " \ | |
"[y/N]: " \ | |
) | |
if prompt_yes_no "$_idr_prompt_msg" "n"; then | |
PREFIX="$($DIRNAME "$_idr_kraft_path")" | |
else | |
say_ok "Already installed." | |
exit 0 | |
fi | |
else | |
if prompt_yes_no "Change the install prefix? [$PREFIX] [y/N]: " "n"; then | |
get_user_response "What should the prefix be? [$PREFIX]: " "$PREFIX" | |
PREFIX="$_RETVAL" | |
fi | |
if [ ! -d "$PREFIX" ]; then | |
if prompt_yes_no "Prefix does not exist, create? [y/N]: " "n"; then | |
do_cmd "$MKDIR -p $PREFIX" | |
else | |
err "Prefix does not exist." | |
fi | |
fi | |
fi | |
_idr_binary="kraft" | |
_idr_archive="kraftkit.tar.gz" | |
downloader "$_idr_url" "$_idr_archive" "$_idr_arch" | |
_CLEANUP_ARCHIVE="$_idr_archive" | |
do_cmd "$TAR -xzf $_idr_archive" | |
_CLEANUP_BINARY="$_idr_binary" | |
do_cmd "$INSTALL $_idr_binary $PREFIX" | |
cleanup | |
_CLEANUP_ARCHIVE="" | |
_CLEANUP_BINARY="" | |
_CLEANUP_VERSION="" | |
} | |
# install_windows_manual installs the kraftkit package for other Windows | |
# distributions using the GitHub binaries. | |
# $1: the architecture of the download | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_windows_manual() { | |
_iwm_arch="$1" | |
_iwm_url="https://github.com/unikraft/kraftkit/issues/267" | |
_iwm_ext=".msi" | |
err "Windows architecture unsupported: $_iwm_arch."\ | |
"You can contribute at $_iwm_url" | |
} | |
# install_kraftkit installs the kraftkit package for the current architecture. | |
# $1: the architecture | |
# $2: whether to install in auto mode | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_kraftkit() { | |
_ikk_arch="$1" | |
_ikk_auto_install="$2" | |
if [ -z "$_ikk_auto_install" ]; then | |
say_debug "Installing kraftkit using package manager for $_ikk_arch" | |
case $_ikk_arch in | |
*"linux-gnu"*) | |
install_linux_gnu | |
install_dependencies_gnu | |
;; | |
*"linux-musl"*) | |
install_linux_musl | |
install_dependencies_musl | |
;; | |
*"darwin"*) | |
install_darwin "$_ikk_arch" | |
# not needed as dependencies are installed by brew | |
# install_dependencies_darwin | |
;; | |
*"windows"*) | |
install_windows "$_ikk_arch" | |
;; | |
*) | |
err "Unsupported architecture: $_ikk_arch" | |
;; | |
esac | |
else | |
need_cmd "$TAR" | |
need_cmd "$INSTALL" | |
case $_ikk_arch in | |
*"linux-gnu"*) | |
install_linux_manual "$_ikk_arch" | |
install_dependencies_gnu | |
;; | |
*"linux-musl"*) | |
install_linux_manual "$_ikk_arch" | |
install_dependencies_musl | |
;; | |
*"darwin"*) | |
install_darwin_manual "$_ikk_arch" | |
;; | |
*"windows"*) | |
install_windows_manual "$_ikk_arch" | |
;; | |
*) | |
err "error: unsupported architecture: $_ikk_arch" | |
;; | |
esac | |
fi | |
} | |
# install_dependencies_gnu installs all kraftkit dependencies needed for | |
# building and running unikernels on gnu distributions. | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_dependencies_gnu() { | |
_idd_list=$(printf "%s %s %s %s %s %s %s %s %s" \ | |
"bison" \ | |
"build-essential" \ | |
"flex" \ | |
"git" \ | |
"libncurses-dev" \ | |
"qemu-system" \ | |
"socat" \ | |
"unzip" \ | |
"wget" \ | |
) | |
if prompt_yes_no "Install recommended dependencies? [Y/n]: " "y"; then | |
return 0 | |
fi | |
need_cmd "$AWK" | |
need_cmd "$GREP" | |
if check_os_release "rhel" || check_os_release "fedora"; then | |
_idd_list=$(printf "%s %s %s %s %s %s %s %s %s" \ | |
"bison" \ | |
"flex" \ | |
"git" \ | |
"ncurses-devel" \ | |
"qemu-system-x86" \ | |
"qemu-system-arm" \ | |
"socat" \ | |
"unzip" \ | |
"wget" \ | |
) | |
_idd_grp_lst="'C Development Tools and Libraries' 'Development Tools'" | |
_idd_no_weak_deps="--setopt=install_weak_deps=False" | |
need_cmd "$YUM" | |
do_cmd "$YUM makecache" | |
say_debug "Installing dependencies: $_idd_list $_idd_grp_lst" | |
do_cmd "$YUM group install $_idd_no_weak_deps -y $_idd_grp_lst" | |
do_cmd "$YUM install $_idd_no_weak_deps -y $_idd_list" | |
elif check_os_release "debian"; then | |
need_cmd "$APT" | |
do_cmd "$APT --allow-unauthenticated update" | |
say_debug "Installing dependencies: $_idd_list" | |
do_cmd "$APT install -y $_idd_list" | |
elif check_os_release "arch"; then | |
_idd_list=$(printf "%s %s %s %s %s %s %s %s %s %s" \ | |
"base-devel" \ | |
"bison" \ | |
"flex" \ | |
"git" \ | |
"ncurses" \ | |
"qemu-system-arm" \ | |
"qemu-system-x86" \ | |
"socat" \ | |
"unzip" \ | |
"wget" \ | |
) | |
need_cmd "$PACMAN" | |
do_cmd "pacman -Syu --noconfirm $_idd_list" | |
else | |
_idd_msg=$(printf "%s%s%s" \ | |
"Unsupported distribution. " \ | |
"Try downloading your architecture-equivalent packages for "\ | |
"this debian list: $_idd_list" \ | |
) | |
err "$_idd_msg" | |
fi | |
return 0 | |
} | |
# install_dependencies_gnu installs all kraftkit dependencies needed for | |
# building and running unikernels on musl distributions. | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_dependencies_musl() { | |
_idm_list=$(printf "%s %s %s %s %s %s %s %s %s %s" \ | |
"bison" \ | |
"build-base" \ | |
"flex" \ | |
"git" \ | |
"ncurses-dev" \ | |
"qemu-system-x86_64" \ | |
"qemu-system-arm" \ | |
"socat" \ | |
"unzip" \ | |
"wget" \ | |
) | |
if prompt_yes_no "Install recommended dependencies? [Y/n]: " "y"; then | |
return 0 | |
fi | |
need_cmd "$AWK" | |
need_cmd "$GREP" | |
if check_os_release "alpine"; then | |
need_cmd "$APK" | |
say_debug "Installing dependencies: $_idm_list" | |
do_cmd "$APK add --no-cache $_idm_list" | |
else | |
_idm_msg=$(printf "%s%s%s" \ | |
"Unsupported distribution. " \ | |
"Try downloading your architecture-equivalent packages for "\ | |
"this alpine list: $_idm_list" \ | |
) | |
err "$_idm_msg" | |
fi | |
return 0 | |
} | |
# install_completions installs the kraftkit completions for the current | |
# environment. | |
# $1: the architecture of the system | |
# Returns: | |
# Code: 0 on success, 1 on error | |
install_completions() { | |
need_cmd "$CAT" | |
need_cmd "$GREP" | |
need_cmd "$MV" | |
need_cmd "$TR" | |
_inc_arch="$1" | |
if ! prompt_yes_no \ | |
"Do you want to install command completions? [Y/n]: y" "y"; then | |
return 0 | |
fi | |
# Check if the shell is supported | |
_inc_shell="" | |
_inc_config_file="" | |
_inc_kraft_config_file="" | |
# Prompt user until they provide a valid answer or exit | |
while true; | |
do | |
get_user_response "What shell are you using? [bash/zsh/fish]: bash" "bash" | |
_inc_answer=$(printf "%s" "$_RETVAL" | "$TR" '[:upper:]' '[:lower:]') | |
if printf "%s" "$_inc_answer" | "$GREP" -q -E "bash"; then | |
_inc_shell="bash" | |
_inc_config_file="$HOME/.bashrc" | |
_inc_kraft_config_file="$HOME/.bash_kraft_completion" | |
break | |
elif printf "%s" "$_inc_answer" | "$GREP" -q -E "zsh"; then | |
_inc_shell="zsh" | |
_inc_config_file="$HOME/.zshrc" | |
_inc_kraft_config_file="$HOME/.zsh_kraft_completion" | |
break | |
elif printf "%s" "$_inc_answer" | "$GREP" -q -E "fish"; then | |
_inc_shell="fish" | |
_inc_config_file="$HOME/.config/fish/config.fish" | |
_inc_kraft_config_file="$HOME/.config/fish/kraft_completion.fish" | |
break | |
elif [ -z "$_inc_answer" ]; then | |
say "No shell provided. Please try again or exit with Ctrl+C\n" | |
else | |
say "Shell %s not supported. " \ | |
"Please try again or exit with Ctrl+C\n" "$_inc_answer" | |
fi | |
done | |
if [ -f "$_inc_kraft_config_file" ]; then | |
if ! prompt_yes_no \ | |
"Completions already exist, overwrite? [Y/n]: " "y"; then | |
return 0 | |
fi | |
fi | |
do_cmd "kraft completion $_inc_shell > $_inc_kraft_config_file" | |
if printf "%s" "$_inc_arch" | "$GREP" -q "darwin"; then | |
printf 'autoload -Uz compinit\ncompinit\n' | | |
"$CAT" - "$_inc_kraft_config_file" > .kraft_tmp && | |
"$MV" .kraft_tmp "$_inc_kraft_config_file" | |
fi | |
say_ok "Done completions enabled by following commands:" | |
say "" | |
say "source $_inc_kraft_config_file;" | |
say "echo 'source $_inc_kraft_config_file;' >> $_inc_config_file;" | |
source $_inc_kraft_config_file; | |
echo "source $_inc_kraft_config_file;" >> $_inc_config_file; | |
} | |
# arg_parse parses the arguments passed to the script. | |
# $@: the arguments to parse | |
# Returns: | |
# NEED_TTY: whether /dev/tty is needed | |
# Code: 0 on success, 1 on error | |
arg_parse() { | |
for _agp_arg in "$@"; do | |
case "$_agp_arg" in | |
--help) | |
usage | |
exit 0 | |
;; | |
--debug) | |
DEBUG=y | |
;; | |
-d) | |
DEBUG=y | |
;; | |
*) | |
OPTIND=1 | |
if [ "${_agp_arg%%--*}" = "" ]; then | |
# Long option (other than --help); | |
# don't attempt to interpret it. | |
continue | |
fi | |
while getopts :hyd _agp_sub_arg "$_agp_arg"; do | |
case "$_agp_sub_arg" in | |
h) | |
usage | |
exit 0 | |
;; | |
y) | |
# user wants to skip the prompt -- | |
# we don't need /dev/tty | |
NEED_TTY=n | |
;; | |
d) | |
# user wants debugging output | |
DEBUG=y | |
;; | |
*) | |
;; | |
esac | |
done | |
;; | |
esac | |
done | |
} | |
# check_autoinstall checks if the user wants to automatically install kraftkit | |
# Returns: | |
# _RETVAL: whether to install automatically: n or empty | |
check_autoinstall() { | |
_cai_auto_install="" | |
if prompt_yes_no "Install KraftKit via package manager? [Y/n]: " "y"; then | |
say_ok "Installing kraftkit via package manager..." | |
else | |
_cai_auto_install="n" | |
fi | |
_RETVAL="$_cai_auto_install" | |
} | |
# main is the entrypoint of the script. | |
# The steps are: | |
# 1. Check if we have all the commands we need | |
# 2. Parse the arguments passed to the script | |
# 3. Detect the architecture and OS of the current machine | |
# 4. Check if the user wants to install kraftkit automatically or not | |
# 5. Install kraftkit for the given architecture step by step | |
# 6. Exit with the appropriate code | |
# $@: the arguments to parse (see usage) | |
# Returns: | |
# Code: 0 on success if kraftkit is installed, something else otherwise | |
main() { | |
# Check if we have all the commands we need | |
downloader --check "" "" | |
need_cmd "$UNAME" | |
need_cmd "$RM" | |
# Check if we have to use /dev/tty to prompt the user | |
NEED_TTY=y | |
arg_parse "$@" | |
say_debug "Parsed arguments: NEED_TTY: $NEED_TTY" | |
# Detect the architecture and OS of the current machine | |
get_architecture || return 1 | |
_main_arch="$_RETVAL" | |
assert_nz "$_main_arch" "arch" | |
say_debug "Detected architecture: $_main_arch" | |
# Check if the user wants to install kraftkit automatically | |
check_autoinstall | |
_main_auto_install="$_RETVAL" | |
say_debug "Auto install: $_main_auto_install" | |
# Install kraftkit for the given architecture and its dependencies | |
install_kraftkit "$_main_arch" "$_main_auto_install" | |
# Check if kraft is installed and working | |
kraft -h | |
_main_kraft_ret="$?" | |
install_completions "$_main_arch" | |
# Install kraftkit completions | |
# if [ "$NEED_TTY" = "y" ]; then | |
# install_completions "$_main_arch" | |
# fi | |
say_ok 'Happy Krafting!' | |
return $_main_kraft_ret | |
} | |
main "$@" || exit 1 | |
# File taken and modified from: https://github.com/rust-lang/rustup | |
# Original credits go to the Rust project and its contributors. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment