Local Mirror Of SmartOS Live Build Environment
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1549 lines
44 KiB

#!/bin/bash
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright 2022 Joyent, Inc.
#
# shellcheck disable=1091
. /lib/sdc/usb-key.sh
eecho() {
echo "$@" 1>&2
}
err() {
eecho "$@"
exit 1
}
fatal() {
eecho
if [[ -n "$1" ]]; then
eecho "ERROR: $1"
fi
eecho
exit 2
}
corrupt() {
eecho "POSSIBLE CORRUPTION:" "$*"
exit 3
}
# Only run in the global zone.
[[ "$(zonename)" == "global" ]] || err "Must run piadm in the global zone"
usage() {
eecho ""
eecho "Usage: piadm [-v] <command> [command-specific arguments]"
eecho ""
eecho " piadm activate|assign <PI-stamp> [ZFS-pool-name]"
eecho " piadm avail"
eecho " piadm bootable [-d] [-e [-i <source>]] [-r] [ZFS-pool-name]"
eecho " piadm install <source> [ZFS-pool-name]"
eecho " piadm list <-H> [ZFS-pool-name]"
eecho " piadm remove <PI-stamp> [ZFS-pool-name]"
eecho " piadm update [ZFS-pool-name]"
err ""
}
not_triton_CN() {
if [[ "$TRITON_CN" == "yes" ]]; then
err "The $1 command cannot be used on a Triton Compute Node"
fi
}
not_triton_HN() {
if [[ "$TRITON_HN" == "yes" ]]; then
eecho "The $1 command cannot be used on a Triton Head Node"
err "On a headnode, please use 'sdcadm platform'."
fi
}
standalone_only() {
not_triton_CN "$@"
not_triton_HN "$@"
}
vecho() {
if [[ $VERBOSE -eq 1 ]]; then
# Verbose echoes invoked by -v go to stdout, not stderr.
echo "$@"
fi
}
declare bootfs
declare -a allbootfs
declare numbootfs
#
# Privilege check. For now, lets just make sure we're root (user 0).
# NOTE: Global zone check was earlier, but some subcommands do NOT
# need privilege, so functionalize that check here for easy naming,
# and potential for more sophistication later.
#
privcheck() {
[[ "$(id -u)" == 0 ]] || err "Must be root for $1"
}
#
# Inventory pools and bootable file systems.
#
getbootable() {
IFS=" "
# Use `mapfile -t` so bash array constructs can work.
mapfile -t allbootfs < <(zpool list -Ho name,bootfs | \
awk '{if ($2 != "-") print $2 }')
numbootfs=${#allbootfs[@]}
}
declare activestamp
activestamp=$(uname -v | sed 's/joyent_//g')
declare installstamp
poolpresent() {
local pool_len cmd
pool_len="${#1}"
# This seems a bit obtuse, but we need to pass the pool specification we
# received on the command line to zpool verbatim, but having an empty
# variable passed to zpool won't give us any valid output.
if (( pool_len == 0 )); then
zp_cmd=( zpool list )
else
zp_cmd=( zpool list "$1" )
fi
if ! "${zp_cmd[@]}" > /dev/null 2>&1 ; then
eecho "Pool $1 not present"
usage
fi
}
# Common-code to obtain the bootable filesystem, and setting $bootfs
# to it. Also checks that the PI stamp or source name is not empty.
# Takes a PI name or source name (which must not be blank) AND a pool
# (which can).
piname_present_get_bootfs() {
if [[ "$1" == "" ]]; then
eecho "Must specify a Platform Image"
usage
fi
poolpresent "$pool"
getbootable
if [[ $numbootfs -gt 1 && "$2" == "" ]]; then
eecho "Multiple bootable pools are available, please specify one"
usage
elif [[ "$2" == "" ]]; then
# If we reach here, no more than one bootable pool.
bootfs=${allbootfs[0]}
if [[ "$bootfs" == "" ]]; then
eecho "No bootable pools available..."
usage
fi
pool=$(echo "$bootfs" | awk -F/ '{print $1}')
vecho "Selecting lone boot pool $pool by default."
else
# If we reach here, the CLI specifies a known-present (passes
# poolpresent()) pool in $2 and we have at least one to check
# against..
pool=$2
bootfs=""
for check in "${allbootfs[@]}"; do
thispool=$(echo "$check" | awk -F/ '{print $1}')
if [[ $thispool == "$pool" ]]; then
bootfs=$check
break
fi
done
if [[ "$bootfs" == "" ]]; then
eecho "Pool $pool does not appear to be bootable."
usage
fi
fi
}
# Defined as a variable in case we need to add parameters (like -s) to it.
# WARNING: Including -k for now.
CURL=( curl -ks -f )
VCURL=( curl -k -f --progress-bar )
vcurl() {
if [[ $VERBOSE -eq 1 ]]; then
# Verbose curls show progress.
"${VCURL[@]}" "$@"
else
# Non-verbose ones do not.
"${CURL[@]}" "$@"
fi
}
# Default well-known source of SmartOS Platform Images
DEFAULT_URL_PREFIX=https://us-east.manta.joyent.com/Joyent_Dev/public/SmartOS/
# Default path for piadm's configuration
PIADM_CONF=/var/piadm/piadm.conf
#
# (Re)-Configure the default URL (and potentially other things in the future)
# using the configuration file (in $PIADM_CONF).
#
config_check() {
# Can't do standalone_only per se as it errors out, but this only
# applies to standalone SmartOS.
if [[ "$TRITON_CN" == "yes" || "$TRITON_HN" == "yes" ]]; then
return
fi
OLD_DEFAULT="$DEFAULT_URL_PREFIX"
if [[ -f $PIADM_CONF ]]; then
. $PIADM_CONF
else
# We're creating $PIADM_CONF.
# NOTE: On an installation this will disappear as /var is
# on the ramdisk.
vecho "Creating $PIADM_CONF"
rm -rf $PIADM_CONF
mkdir -p /var/piadm
PIADM_CONFIG_VERSION=1
cat <<EOF > $PIADM_CONF
PIADM_CONFIG_VERSION=$PIADM_CONFIG_VERSION
DEFAULT_URL_PREFIX=$DEFAULT_URL_PREFIX
EOF
fi
# Reality checks for PIADM_CONFIG_VERSION and more.
# Currently we only have one version. In the future, we will need to
# change that. We will do strict string comparisons too, instead of
# numeric ones, to harden against corrupt piadm.conf files.
#
if [[ "$PIADM_CONFIG_VERSION" == "1" ]]; then
if [[ $VERBOSE -eq 1 ]]; then
echo "Version 1 of $PIADM_CONF"
echo "The following file contents have been configured:"
echo ""
cat $PIADM_CONF
echo ""
if [[ "$OLD_DEFAULT" != "$DEFAULT_URL_PREFIX" ]]; then
echo "DEFAULT_URL_PREFIX was $OLD_DEFAULT ,"
echo "but now is $DEFAULT_URL_PREFIX"
fi
fi
else
eecho "WARNING: Bad config file version: $PIADM_CONFIG_VERSION"
err "Please fix, or delete, $PIADM_CONF and run again"
fi
# Can furthermore be overridden by the user's PIADM_URL_PREFIX.
URL_PREFIX=${PIADM_URL_PREFIX:-${DEFAULT_URL_PREFIX}}
}
avail() {
# For now, assume that the URL_PREFIX points to a Manta
# back-end and we use Manta methods for querying (and json(1)
# to help us out). If the user overrides with
# PIADM_URL_PREFIX, the behavior is undefined, and we issue a
# warning.
if [[ "$URL_PREFIX" != "$DEFAULT_URL_PREFIX" ]]; then
eecho "WARNING: $URL_PREFIX is being queried for available"
eecho "platform images. Output may be empty, or unusual."
eecho ""
fi
# The aforementioned Manta method, parsed by json(1).
# Don't print ones old enough to NOT contain piadm(8) itself.
# Always be silent (i.e. use ${CURL[@]}).
"${CURL[@]}" "${URL_PREFIX}/?limit=1000" | json -ga -c \
"this.name.match(/Z$/) && this.name>=\"$activestamp\"" name
}
# Scan for available installation media and mount it.
mount_installmedia() {
tfile=$(mktemp)
tfile2=$(mktemp)
mntdir=$1
# Try the USB key first, quietly and without $mntdir/.joyentusb check
if ! mount_usb_key "$mntdir" skip > "$tfile" 2>&1 ; then
# If the USB key fails, try mounting the ISO.
if ! mount_ISO "$mntdir" > "$tfile2" 2>&1; then
if [[ $VERBOSE -eq 1 ]]; then
eecho "Can't find install media: USB stick errors:"
eecho ""
cat "$tfile" 1>&2
eecho ""
eecho "ISO errors"
eecho ""
cat "$tfile2" 1>&2
fi
rm -f "$tfile" "$tfile2"
return 1
fi
fi
rm -f "$tfile" "$tfile2"
return 0
}
# Install a Platform Image.
#
# XXX WARNING - there is a security discussion to be had about the integrity
# of the source.
install() {
piname_present_get_bootfs "$1" "$2"
tdir=$(mktemp -d)
mkdir "${tdir}/mnt"
# $1 contains a "source". Deal with it correctly in the big
# if/elif/else block. Once done, we can copy over bits into $tdir or
# ${tdir}/mnt.
#
# Special-case of "latest"
if [[ "$1" == "latest" ]]; then
# Well-known URL for the latest PI using conventions from
# URL_PREFIX. Grab the latest-version ISO. Before proceeding,
# make sure it's the current one.
iso=yes
vecho "Downloading latest SmartOS ISO"
vcurl -o "${tdir}/smartos.iso" "${URL_PREFIX}/smartos-latest.iso"
code=$?
if [[ $code -ne 0 ]]; then
/bin/rm -rf "${tdir}"
fatal "Curl exit code $code"
fi
mount -F hsfs "${tdir}/smartos.iso" "${tdir}/mnt"
# For now, assume boot stamp and PI stamp are the same on an ISO...
stamp=$(cat "${tdir}/mnt/etc/version/boot")
elif [[ "$1" == "media" ]]; then
# Scan the available media to find what we seek. Same advice
# about making sure it's the current one.
iso=yes
if ! mount_installmedia "${tdir}/mnt" ; then
/bin/rm -rf "${tdir}"
err "Cannot find install media"
fi
# For now, assume boot stamp and PI stamp are the same on
# install media.
stamp=$(cat "${tdir}/mnt/etc/version/boot")
elif [[ -f $1 ]]; then
# File input! Check for what kind, etc. etc.
# WARNING: Depends GREATLY on the output of file(1)
filetype=$(file "$1" | awk '{print $2}')
if [[ "$filetype" == "ISO" ]]; then
# Assume .iso file.
vecho "Treating $1 as an ISO file."
iso=yes
mount -F hsfs "$1" "${tdir}/mnt"
stamp=$(cat "${tdir}/mnt/etc/version/boot")
elif [[ "$filetype" == "gzip" ]]; then
# SmartOS PI. Let's confirm it's actually a .tgz...
if ! gtar -xzOf "$1" > /dev/null 2>&1; then
/bin/rm -rf "${tdir:?}"
err "File $1 is not an ISO or a .tgz file."
fi
# We're most-likely good here. NOTE: SmartOS/Triton
# PI files expand to platform-$STAMP. Fix it here
# before proceeding.
vecho "Treating $1 as an .tgz Platform Image file."
gtar -xzf "$1" -C "${tdir}/mnt"
mv "${tdir}"/mnt/platform-* "${tdir}/mnt/platform"
iso=no
stamp=$(cat "${tdir}/mnt/platform/etc/version/platform")
else
/bin/rm -rf "${tdir:?}"
err "Unknown file type for $1"
fi
else
# Explicit boot stamp or URL.
# Do a URL reality check.
vecho "Attempting download of URL $1"
vcurl -o "${tdir}/download" "$1"
if [[ -e ${tdir}/download ]]; then
# Recurse with the downloaded file.
dload=$(mktemp)
mv -f "${tdir}/download" "$dload"
/bin/rm -rf "${tdir}"
# in case `install` exits out early...
( pwait $$ ; rm -f "$dload" ) &
vecho "Installing $1"
vecho " (downloaded to $dload)"
install "$dload" "$2"
return 0
fi
# Else we treat it like a boot stamp.
# Now that we think it's a boot stamp, check if it's the
# current one or if it exists.
if [[ -d ${bootfs}/platform-${1} ]]; then
/bin/rm -rf "${tdir}"
eecho "PI-stamp $1 appears to be already on /${bootfs}"
err "Use piadm remove $1 to remove any old copies."
fi
# Confirm this is a legitimate build stamp.
# Use conventions from site hosted in URL_PREFIX.
vecho "Downloading ISO for Platform Image $1"
checkurl=${URL_PREFIX}/$1/index.html
# Always be silent, use ${CURL[@]}.
if ! "${CURL[@]}" "$checkurl" | head | grep -qv "not found" ; then
eecho "PI-stamp $1" \
"is invalid for download from $URL_PREFIX"
usage
fi
vcurl -o "${tdir}/smartos.iso" "${URL_PREFIX}/$1/smartos-${1}.iso"
code=$?
if [[ $code -ne 0 ]]; then
/bin/rm -rf "${tdir}"
fatal "PI-stamp $1 -- curl exit code $code"
fi
mount -F hsfs "${tdir}/smartos.iso" "${tdir}/mnt"
code=$?
if [[ $code -ne 0 ]]; then
/bin/rm -rf "${tdir}"
fatal "PI-stamp $1 -- mount exit code $code"
fi
iso=yes
stamp=$1
# Reality-check boot stamp.
bstamp=$(cat "${tdir}/mnt/etc/version/boot")
if [[ $stamp != "$bstamp" ]]; then
umount "${tdir}/mnt"
/bin/rm -rf "${tdir}"
err "Boot bits stamp says $bstamp," \
"vs. argument stamp $stamp"
fi
fi
vecho "Installing PI $stamp"
# At this point we have ${tdir}/mnt which contains at least
# "platform". If "iso" is yes, it also contains "boot",
# "boot.catalog" and "etc", but we only really care about boot.catalog
# and boot. These may be mounted as read-only, so we can't do mv.
if [[ "$iso" == "yes" ]]; then
# Match-check boot stamp and platform stamp.
pstamp=$(cat "${tdir}/mnt/platform/etc/version/platform")
if [[ "$stamp" != "$pstamp" ]]; then
umount "${tdir}/mnt"
/bin/rm -rf "${tdir}"
err "Boot stamp $stamp mismatches platform stamp" \
"$pstamp"
fi
if [[ -e "/${bootfs}/boot-${stamp}" ]]; then
umount "${tdir}/mnt"
/bin/rm -rf "${tdir}"
eecho "PI-stamp $stamp has boot bits already" \
"on /${bootfs}"
err "Use piadm remove $stamp " \
"to remove any old copies."
fi
mkdir "/${bootfs}/boot-${stamp}" || \
eecho "Can't mkdir /${bootfs}/boot-${stamp}"
tar -cf - -C "${tdir}/mnt/boot" . | \
tar -xf - -C "/${bootfs}/boot-${stamp}" || \
eecho "Problem in tar of boot bits"
[[ -e "/${bootfs}/custom/loader.conf.local" ]] && \
ln -sf "../custom/loader.conf.local" \
"/${bootfs}/boot-${stamp}/loader.conf.local"
[[ -e "/${bootfs}/custom/loader.rc.local" ]] && \
ln -sf "../custom/loader.rc.local" \
"/${bootfs}/boot-${stamp}/loader.rc.local"
fi
if [[ -e /${bootfs}/platform-${stamp} ]]; then
if [[ $iso == "yes" ]]; then
umount "${tdir}/mnt"
fi
/bin/rm -rf "${tdir}"
eecho "PI-stamp $stamp appears to be already on /${bootfs}"
err "Use piadm remove $stamp to remove any old copies."
fi
mkdir "/${bootfs}/platform-${stamp}" || \
eecho "Can't mkdir /${bootfs}/platform-${stamp}"
tar -cf - -C "${tdir}/mnt/platform" . | \
tar -xf - -C "/${bootfs}/platform-${stamp}" || \
eecho "Problem in tar of platform bits"
if [[ "$iso" == "yes" ]]; then
umount "${tdir}/mnt"
fi
/bin/rm -rf "${tdir:?}"
if [[ ! -d /${bootfs}/platform-${stamp} ]]; then
fatal "Installation problem (no ${bootfs}/platform-${stamp})"
fi
if [[ ! -d /${bootfs}/boot-${stamp} && "$iso" == "yes" ]]; then
fatal "Installation problem (no ${bootfs}/boot-${stamp}" \
"from ISO)"
fi
# Global variable for enablepool() usage...
installstamp=$stamp
return 0
}
list() {
if [[ $1 == "-H" ]]; then
pool=$2
else
printf "%-22s %-30s %-10s %-4s %-4s \n" "PI STAMP" \
"BOOTABLE FILESYSTEM" "BOOT IMAGE" "NOW" "NEXT"
pool=$1
fi
poolpresent "$pool"
getbootable
for bootfs in "${allbootfs[@]}"; do
bfspool=$(echo "$bootfs" | awk -F/ '{print $1}')
if [[ "$pool" != "" && "$bfspool" != "$pool" ]]; then
# If we specify a pool for listing, skip ones not in
# the pool.
continue
fi
cd "/$bootfs" || fatal "Could not chdir to /$bootfs"
bootbitsstamp=$(cat etc/version/boot)
# Triton Head Nodes are special.
if [[ "$TRITON_HN" != "yes" ]]; then
# Regular standalone SmartOS case.
if [[ ! -L /$bootfs/platform ]]; then
corrupt "WARNING: Bootable filesystem" \
"$bootfs has non-symlink platform"
fi
bootstamp=$(cat platform/etc/version/platform)
mapfile -t pis \
< <(cat platform-*/etc/version/platform)
else
# Triton Head Node case.
if [[ ! -d /$bootfs/os ]]; then
corrupt "WARNING: Headnode boot filesystem" \
"$bootfs has no os/ directory."
fi
bootstamp=$(awk -F= '/^platform-version=/ {print $2}' \
< boot/loader.conf | sed 's/"//g')
mapfile -t pis < <(cat os/*/platform/etc/version/platform)
fi
for pi in "${pis[@]}"; do
if [[ $activestamp == "$pi" ]]; then
active="yes"
else
active="no"
fi
if [[ $bootstamp == "$pi" ]]; then
booting="yes"
else
booting="no"
fi
if [[ $bootbitsstamp == "$pi" ]]; then
bootbits="next"
elif [[ -d "boot-$pi" ]]; then
bootbits="available"
# Special-case of ipxe booting next needs "next"
if [[ $pi == "ipxe" ]]
then
if [[ $VERBOSE -eq 1 ]]; then
pi="ipxe($(cat etc/version/ipxe))"
else
pi="ipxe"
fi
if [[ $booting == "yes" ]]; then
bootbits="next"
fi
fi
else
bootbits="none"
fi
printf "%-22s %-30s %-10s %-4s %-4s\n" \
"$pi" "$bootfs" "$bootbits" "$active" "$booting"
done
done
}
update_boot_sectors() {
pool=$1
bootfs=$2
flag=$3
# XXX WARNING -- illumos#12894 will allow slogs. We will need to
# alter the generation of boot_devices accordingly. Generate the
# pool's boot devices now, in case we did something hyper-clever for
# the pool. s1 may be created, but not yet PCFS...
mapfile -t boot_devices < <(zpool list -vHP "$pool" | \
grep -E 'c[0-9]+' | awk '{print $1}' | sed -E 's/s[0-9]+//g')
# Reality check the pool was created with -B.
# First way to do this is to check for the `bootsize` property not
# its default, which is NO bootsize.
if [[ $(zpool list -Ho bootsize "$pool") == "-" ]]; then
# No bootsize is a first-cut test. It passes if the pool was
# created with `zpool create -B`. There's one other that needs
# to be performed, because some bootable pools are manually
# configured to share slices with other functions (slog,
# l2arc, dedup):
# Use fstyp to confirm if this is a manually created EFI
# System Partition (ESP)
type=$(fstyp "/dev/dsk/${boot_devices[0]}s0")
if [[ "$type" == "pcfs" ]]; then
# If we detect PCFS on s0, it's LIKELY an EFI System
# Partition that was crafted manually. Use s1 if it's
# ZFS, or bail if it's not.
s1type=$(fstyp "/dev/dsk/${boot_devices[0]}s1")
if [[ "$s1type" != "zfs" ]]; then
fatal "Unusual configuration," \
"${boot_devices[0]}s1 not ZFS"
fi
suffix=s1
else
suffix=s0
fi
else
# Guaranteed that s0 is EFI System Partition, ZFS lives on s1.
suffix=s1
fi
some=0
for a in "${boot_devices[@]}"; do
if [[ "$flag" == "-d" ]]; then
if [[ "$suffix" == "s0" ]]; then
# BIOS boot, we don't care.
some=1
continue
fi
# otherwise mount the ESP and trash it.
tdir=$(mktemp -d)
if ! mount -F pcfs "/dev/dsk/${a}s0" "${tdir:?}" ; then
# Wrong filesystem, so skip the rest of this loop
eecho "disk $a has no PCFS ESP, it seems"
continue
fi
# Just take out the EFI directory, in case someone
# is using it for something ELSE also.
/bin/rm -rf "${tdir}/EFI"
umount "$tdir" && rmdir "$tdir"
some=1
# If we make it here, at least some disks had
# ESP and we managed to clean them out. "some" below
# will get set.
else
# Plow through devices, even if some fail.
# installboot also does
# loader-into-EFI-System-Partition this way.
# Trailing / is important in the -b argument
# because boot is actually a symlink.
if installboot -m -b "/${bootfs}/boot/" \
"/${bootfs}/boot/pmbr" \
"/${bootfs}/boot/gptzfsboot" \
"/dev/rdsk/${a}${suffix}" > /dev/null 2>&1 ; then
some=1
else
eecho "WARNING: Can't installboot on ${a}${suffix}"
fi
fi
done
# Partial success (altering some of the pool's disks) is good
# enough for command success.
if [[ $some -eq 0 ]]; then
fatal "Could not modify ANY vdevs of pool $2"
fi
}
#
# Emit a select-a-Platform-Image page header. Set the menuset prefix!
#
# PWD is /${bootfs} at this point.
#
emit_pageheader() {
pagenum=$1
totalpages=$2
defaultpi=$3
if [[ "$pagenum" == "$totalpages" ]]; then
nextpage=3
else
nextpage=$((pagenum + 3))
fi
menusetnum=$((pagenum + 2))
menusetprefix="pi${pagenum}menu_"
cat >> ./os/pi.rc <<EOF
set menuset_name${menusetnum}="pi${pagenum}"
set ${menusetprefix}init[1]="init_pi"
set ${menusetprefix}caption[1]="Back to Main Menu [Backspace]"
set ${menusetprefix}command[1]="pi_draw_screen drop 1 goto_menu"
set ${menusetprefix}keycode[1]=8
set pi${pagenum}ansi_caption[1]="Back to Main Menu ^[1m[Backspace]^[m"
set ${menusetprefix}caption[2]="[P]age: ${pagenum} of ${totalpages}"
set ${menusetprefix}command[2]="${nextpage} goto_menu"
set ${menusetprefix}keycode[2]=112
set pi${pagenum}ansi_caption[2]="^[1mP^[mage: ${pagenum} of ${totalpages}"
set ${menusetprefix}options=3
EOF
# Need printf(1) so the shell doesn't attempt expansion...
printf 'set %soptionstext="${pimenu_optionstext}"\n' \
$menusetprefix >> ./os/pi.rc
cat >> ./os/pi.rc <<EOF
set ${menusetprefix}caption[3]="[D]efault (${defaultpi})"
set ${menusetprefix}command[3]="s\" set bootpi=default\" evaluate pi_unload pi_draw_screen"
set ${menusetprefix}keycode[3]=100
set pi${pagenum}ansi_caption[3]="^[1mD^[mefault (${defaultpi})"
EOF
echo $menusetprefix
}
#
# Create the /$bootfs/os/ directory from scratch. This includes a
# /bin/rm -rf of the old /$bootfs/os/ directory if it exists.
#
# The os/ directory, inspired by the Triton Head Node, allows a
# selection of specific platform images that aren't the "default" as
# symlinked into /$bootfs/platform/. It is comprised of:
#
# os/pi.rc --> Generated by this function, it is a list of up to three
# menu pages of alternate platform images, NOT INCLUDING the "default"
# one per above. If $bootfs has too many PIs, this function should
# take the most recent as sorted by stamp date.
#
# os/$STAMP/platform --> symbolic links to ../../platform-$STAMP/.
# Because "platform" is the last path component this way, the kernel
# will not seek a boot archive in the default /$bootfs/platform directory.
# NOTE that the default one wll not have a os/$DEFAULTSTAMP/platform entry.
#
# PWD is /${bootfs} at this point.
#
regenerate_os() {
# Clobber old one now, generate a new one, and setup the extra
# main-menu item..
vecho "Removing old ./os/ directory"
/bin/rm -rf ./os
# Only use "maxpis" newest PI stamps, which must be 15 or fewer.
# Note that the default PI is not counted amongst the 15.
maxpis=15
pisperpage=5
defaultpi=$(cat ./platform/etc/version/platform)
mapfile -t pis < <(cat platform-*/etc/version/platform | \
grep -v ${defaultpi} | sort -r | head -${maxpis})
totalpis=${#pis[@]}
if [[ "$totalpis" == "0" ]]; then
vecho "No need for the ./os/ directory, only one PI"
return
fi
vecho "Creating new ./os/ directory"
mkdir ./os
cat > ./os/pi.rc <<EOF
\\
\\ Generated by piadm(8).
\\
\\ Assume mainmenu_options=4 for now.
set mainmenu_caption[5]="Alternate [P]latform Images..."
set mainmenu_command[5]="3 goto_menu"
set mainmenu_keycode[5]=112
set mainansi_caption[5]="Alternate ^[1mP^[mlatform Images..."
\\ Will be reset by init_pi (see below and in menu-commands.4th).
set pimenu_optionstext="Platform Image: (UNINIT)"
\\ For feeding init_pi.
set pitext="Platform Image: "
EOF
# Make sure maxpis is evenly divisible by pisperpage. maxpages
# should not exceed 3 (loader limits 8 total pages, before this we've
# eaten 4, so eat 3 more, and save one for later).
maxpages=$(((maxpis + 1) / pisperpage))
totalpages=$((((totalpis - 1) / pisperpage) + 1))
pinum=0
menusetprefix=""
for pi in "${pis[@]}"; do
vecho "Including Platform Image ${pi}"
mkdir ./os/${pi}
ln -s ../../platform-${pi} ./os/${pi}/platform
pagenum=$((((pinum) / pisperpage) + 1))
itemnum=$(((pinum % pisperpage) + 1))
if [[ "$itemnum" == "1" ]]; then
menusetprefix=$(emit_pageheader $pagenum $totalpages \
$defaultpi)
fi
# else menusetprefix is already set for this round!
# Emit the entry.
cat >> ./os/pi.rc <<EOF
set ${menusetprefix}caption[$((itemnum + 3))]="${pi}"
set ${menusetprefix}command[$((itemnum + 3))]="pi_unload s\" set bootpi=${pi}\" evaluate s\" load /os/${pi}/platform/i86pc/kernel/amd64/unix\" evaluate s\" load -t rootfs /os/${pi}/platform/i86pc/amd64/boot_archive\" evaluate pi_draw_screen"
set pi${pagenum}ansi_caption[$((itemnum + 3))]="${pi}"
EOF
pinum=$((pinum + 1))
done
}
activate() {
pistamp=$1
piname_present_get_bootfs "$pistamp" "$2"
pool=$(echo "$bootfs" | awk -F/ '{print $1}')
cd "/$bootfs" || fatal "Could not chdir to /$bootfs"
if [[ -d "platform-$pistamp" ]]; then
if [[ -f platform/etc/version/platform ]]; then
bootstamp=$(cat platform/etc/version/platform)
else
bootstamp=""
fi
if [[ $bootstamp == "$pistamp" ]]; then
vecho "NOTE: $pistamp is the current active PI."
regenerate_os
return
fi
else
eecho "$pistamp is not a stamp for a PI on pool $pool"
usage
fi
vecho "Platform Image $pistamp will be loaded on next boot,"
# Okay, at this point we have the platform sorted out. Let's see if
# we can do the same with the boot.
if [[ -d boot-$pistamp ]]; then
rm -f boot
ln -s ./boot-"$pistamp" boot
mkdir -p etc/version
echo "$pistamp" > etc/version/boot
update_boot_sectors "$pool" "$bootfs"
# Fix the loader.conf for keep-the-ramdisk booting.
grep -q 'fstype="ufs"' ./boot/loader.conf || \
echo 'fstype="ufs"' >> ./boot/loader.conf
vecho " with a new boot image,"
else
vecho " WARNING: $pistamp has no matching boot image, using"
if [[ ! -f etc/version/boot ]]; then
fatal "No boot version available on /$bootfs"
elif [[ ! -d boot/. ]]; then
fatal "No boot bits directory on /$bootfs"
fi
fi
vecho " boot image " "$(cat etc/version/boot)"
rm -f platform
ln -s "./platform-$pistamp" platform
regenerate_os
}
remove() {
pistamp=$1
piname_present_get_bootfs "$pistamp" "$2"
cd "/$bootfs" || fatal "Could not chdir to /$bootfs"
bootstamp=$(cat platform/etc/version/platform)
if [[ -d platform-$pistamp ]]; then
if [[ $bootstamp == "$pistamp" ]]; then
eecho "$pistamp is the next-booting PI." \
"Please activate another PI"
eecho "using 'piadm activate <other-PI-stamp>' first."
usage
fi
# Boot image processing.
if [[ -d "boot-$pistamp" ]]; then
# Boot bits may be older than the current PI, and the
# current PI may not have matching boot bits for some
# reason. Guard against shooting yourself in the foot.
if grep -q "$pistamp" etc/version/boot; then
eecho "$pistamp is the current set of boot" \
"binaries. Please"
eecho "activate another pi using" \
"'piadm activate <other-PI-stamp>'" \
"first."
usage
fi
/bin/rm -rf "boot-$pistamp"
fi
/bin/rm -rf "platform-$pistamp"
regenerate_os
else
eecho "$pistamp is not a stamp for a PI on pool $pool"
usage
fi
}
ispoolenabled() {
pool=$1
poolpresent "$pool"
# SmartOS convention is $POOL/boot.
currbootfs=$(zpool list -Ho bootfs "$pool")
if [[ $currbootfs == "${pool}/boot" ]]; then
# We're bootable (at least bootable enough)
zfs list -H "$currbootfs" > /dev/null 2>&1 && return 0
# else drop out to not-bootable, but this shouldn't happen.
vecho ".... odd, ${pool}/boot is pool's bootfs," \
"but isn't a filesystem"
elif [[ $currbootfs != "-" ]]; then
eecho "It appears pool $pool has a different boot filesystem" \
"than the"
eecho "standard SmartOS filesystem of ${pool}/boot. It will" \
"need manual"
corrupt "intervention."
fi
# Not bootable.
return 1
}
# Routines and variables related specifically to Triton Compute Nodes.
# Data for Triton Compute Node (CN) iPXE.
TRITON_IPXE_PATH=/opt/smartdc/share/usbkey/contents
TRITON_IPXE_ETC=${TRITON_IPXE_PATH}/etc
TRITON_IPXE_BOOT=${TRITON_IPXE_PATH}/boot
initialize_as_CN() {
TRITON_CN="yes"
source /lib/sdc/config.sh
load_sdc_config
# Establish the CNAPI default boot Platform Image
# Always be silent, use ${CURL[@]}.
cnapi_domain=$("${CURL[@]}" http://"${CONFIG_sapi_domain:?}"/applications?name=sdc | json -Ha metadata.cnapi_domain)
CNAPI_DEFAULT_PI=$("${CURL[@]}" http://"${cnapi_domain}"/boot/default | json platform)
}
initialize_as_HN() {
#
# For the Triton Head Node, we only really want piadm doing one of
# four things:
#
# 1.) Enabling a bootable pool, which will involve a bunch of
# Triton-savvy maneuvers.
# 2.) Updating the boot sector and/or EFI boot.
# 3.) Disabling a bootable pool, which will ALSO involved a bunch of
# Triton-savvy maneuvers.
# 4.) List the pools available that are Triton-savvy and bootable.
#
# For right now, we merely need to indicate we're a headnode and
# if we're booted off of a pool, which one.
TRITON_HN="yes"
TRITON_HN_BOOTPOOL=$(bootparams | awk -F= '/^triton_bootpool=/ {print $2}')
}
# README file for /${bootfs}/platform-ipxe/README.
cat_readme() {
cat <<EOF
For iPXE boots, the platform/ directory is empty. This README, and
the word "ipxe" in platform/etc/version/platform, are here so there's
something in the platform/ directory to prevent piadm (especially
older versions) from thinking something is wrong.
EOF
}
# Given a bootstamp, install a Platform Image as a backup for the compute node.
# PWD is /${bootfs} at this point.
install_pi_CN() {
vecho "Installing as a backup Platform image PI stamp $1"
if [[ -d ./platform-$1 ]]; then
vecho "PI stamp $1 already installed."
installstamp=$1
return 0
fi
# Obtain at least unix and boot archive.
# For now, use bootparams to get the URL needed, and pull
# files from there. If there's a better way to obtain things, use it.
unix_path=$(bootparams | awk -F= '/^boot-file=/ {print $2}')
if [[ "$1" == "$CNAPI_DEFAULT_PI" ]]; then
# We need to edit out the bootstamp part. Count on path
# having "os/STAMP/" in it.
unix_path=$(sed "s/os\/[0-9TZ]*\//os\/$CNAPI_DEFAULT_PI\//g" <<< "$unix_path")
fi
archive_prefix=$(sed 's/kernel\/amd64\/unix/amd64/g' <<< "$unix_path")
# Reality check the buildstamp passed, which will become installstamp,
# is in the unix_path.
echo "$unix_path" | grep -q "$1" || return 1
installstamp=$1
vecho "making platform-$installstamp directories"
mkdir -p platform-"$installstamp"/etc/version
echo "$installstamp" > platform-"$installstamp"/etc/version/platform
mkdir -p platform-"$installstamp"/i86pc/kernel/amd64
mkdir -p platform-"$installstamp"/i86pc/amd64
# To enable a platform/ component in the boot file pathname to confirm
# "unix" is also in the boot archive (as /platform/..../unix).
ln -s . platform-"$installstamp"/platform
vecho "Pulling unix"
vcurl "$unix_path" > \
platform-"$installstamp"/i86pc/kernel/amd64/unix || return 1
for file in boot_archive boot_archive.hash boot_archive.manifest \
boot_archive.gitstatus; do
vecho "Pulling" "$file"
vcurl "${archive_prefix}"/"${file}" > \
platform-"$installstamp"/i86pc/amd64/"${file}" || \
return 1
done
return 0
}
# Enabling a bootable pool, specifically for a Triton Compute Node.
bringup_CN() {
# Bootfs is already set at this point.
if [[ "$CNAPI_DEFAULT_PI" != "$activestamp" ]]; then
vecho "Current booted PI $activestamp is not default PI" \
"$CNAPI_DEFAULT_PI"
fi
# First install ipxe in $bootfs.
cd "/${bootfs}" || fatal "Could not chdir to /$bootfs"
# Clobber everything in $bootfs. We do not care about dot-files.
rm -rf ./*
# The "platform-ipxe" directory for on-disk iPXE is a placeholder.
# We put a README (see cat_readme() above) and the string "ipxe"
# for the PI-stamp.
mkdir -p ./platform-ipxe/etc/version
cat_readme > ./platform-ipxe/README
echo "ipxe" > ./platform-ipxe/etc/version/platform
# Now we set up the "etc" directory, which contains versions of
# both loader ("boot") and iPXE ("ipxe").
mkdir -p etc/version
cp -f ${TRITON_IPXE_ETC}/version/* etc/version/.
vecho "installing ipxe version: " "$(cat etc/version/ipxe)"
# Now we set up the "boot-ipxe" directory.
mkdir boot-ipxe
# Use tar here because it's the first time.
tar -cf - -C ${TRITON_IPXE_BOOT} . | tar -xf - -C boot-ipxe
# Preserve versions in boot-ipxe too in case we need them later.
cp -f etc/version/boot boot-ipxe/bootversion
cp -f etc/version/ipxe boot-ipxe/ipxeversion
# Symlinks for loader default and `piadm list` consistency.
ln -s platform-ipxe platform
ln -s boot-ipxe boot
# Install a PI for backup booting purposes.
if ! install_pi_CN "$activestamp" && \
[[ "$CNAPI_DEFAULT_PI" != "$activestamp" ]]; then
/bin/rm -rf platform-"$activestamp"
if ! install_pi_CN "$CNAPI_DEFAULT_PI"; then
/bin/rm -rf platform-"$CNAPI_DEFAULT_PI"
err "No PIs available"
fi
fi
# installstamp will be set by the successful install_pi_CN()
# Populate loader.conf.
# NOTE: One could uncomment the sed below and replace the cp if one
# wished to have the CN backup-boot not go into the Triton HN
# installer but act in a different kind of weird way.
# sed 's/headnode="true"/headnode="false"/g' \ <
# boot-ipxe/loader.conf.tmpl > boot-ipxe/loader.conf
cp boot-ipxe/loader.conf.tmpl boot-ipxe/loader.conf
{
echo 'ipxe="true"'
echo 'smt_enabled="true"'
echo 'console="ttyb,ttya,ttyc,ttyd,text"'
echo 'os_console="ttyb"'
echo 'fstype="ufs"'
# use $installstamp to help!
echo "platform-version=$installstamp"
# Need an extra "platform" in here to satisfy the illumos
# load-time that looks for a unix in the boot archive. The
# boot file path MUST have /platform/i86pc/kernel/amd64/unix
# as its trailing components. See install_pi_CN() for the
# insertion of a symlink to help out.
echo bootfile=\"/platform-"$installstamp"/platform/i86pc/kernel/amd64/unix\"
echo boot_archive_name=\"/platform-"$installstamp"/i86pc/amd64/boot_archive\"
echo boot_archive.hash_name=\"/platform-"$installstamp"/i86pc/amd64/boot_archive.hash\"
} >> boot-ipxe/loader.conf
# Caller will invoke update_boot_sectors.
}
update_CN() {
declare -a pdirs
if [[ "$TRITON_CN" != "yes" ]]; then
err "The update command may only be used on a Triton Compute Node"
fi
piname_present_get_bootfs "ipxe" "$1"
cd "/${bootfs}" || fatal "Could not chdir to /$bootfs"
# First check if the backup PI is in need of update.
# The standard iPXE/CN deployment has exactly one platform-STAMP.
mapfile -t pdirs < <(ls -d platform-[0-9]*T*Z)
if [[ ${#pdirs[@]} -gt 1 ]]; then
corrupt "Multiple platform-STAMP in CN bootfs /${bootfs}/."
elif [[ ${#pdirs[@]} -lt 1 ]]; then
corrupt "No platform-STAMP in CN bootfs /${bootfs}/."
fi
pdir=${pdirs[0]}
pstamp=$(cat "${pdir}"/etc/version/platform)
if [[ "$pstamp" != "$activestamp" ]]; then
vecho "Updating backup PI to" "$activestamp"
if ! install_pi_CN "$activestamp" && \
[[ "$CNAPI_DEFAULT_PI" != "$activestamp" ]]; then
vecho "...trying" "$CNAPI_DEFAULT_PI" "instead"
if ! install_pi_CN "$CNAPI_DEFAULT_PI" ; then
/bin/rm -rf platform-"$activestamp"
/bin/rm -rf platform-"$CNAPI_DEFAULT_PI"
err "No PIs available, keeping" "$pstamp"
fi
fi
# Success, don't keep the old one!
# $installstamp will have new PI stamp, will inform below.
vecho "...success installing $installstamp"
/bin/rm -rf "$pdir"
tfile=$(mktemp)
# Alter loader.conf's backup PI stamp.
# NOTE: If loader.conf has a flag day, we really need to
# Do Better here.
cp ./boot-ipxe/loader.conf "${tfile}"
sed s/"${pstamp}"/"${installstamp}"/g < "${tfile}" \
> ./boot-ipxe/loader.conf
rm -f "${tfile}"
fi
# THEN check to see if we need to update iPXE...
diskipxe=$(cat etc/version/ipxe)
diskboot=$(cat etc/version/boot)
newipxe=$(cat ${TRITON_IPXE_ETC}/version/ipxe)
newboot=$(cat ${TRITON_IPXE_ETC}/version/boot)
if [[ "$diskipxe" == "$newipxe" && "$diskboot" == "$newboot" ]]; then
vecho "No updates needed for iPXE and its loader."
vecho "If you think there should be an update, run"
vecho "'sdcadm experimental update-gz-tools' on your"\
"Triton Head Node"
exit 0
fi
[[ "$diskipxe" != "$newipxe" ]] && \
vecho "Updating iPXE provided by headnode (ver: $newipxe)"
[[ "$diskboot" != "$newboot" ]] && \
vecho "Updating boot loader provided by headnode (ver: $newboot)"
cp -f ${TRITON_IPXE_ETC}/version/* etc/version/.
# NOTE: If there is an illumos loader flag day, one may have to
# perform more than simple rsync to update ./boot/.
rsync -r ${TRITON_IPXE_BOOT}/. ./boot/.
# Preserve versions in boot-ipxe too in case we need them later.
cp -f etc/version/boot boot-ipxe/bootversion
cp -f etc/version/ipxe boot-ipxe/ipxeversion
# And make sure we update the boot sectors.
update_boot_sectors "$pool" "$bootfs"
}
bringup_HN() {
# One last reality check...
# 1.) Check if we're trying to enable our currently booting pool.
if [[ "$pool" == "$TRITON_HN_BOOTPOOL" ]]; then
err "Pool $pool is already bootable, and we just booted it."
fi
# If we reach here, we've checked for the already-bootable
# case, checked for the can-be-bootable case, and have a
# $bootfs ready to go. For the head node, we will be extra
# cautious and remove all of its contents first. NOTE: rm(1)
# will complain because you'll get EBUSY for the mountpoint
# itself.
vecho "Cleaning up $bootfs"
/bin/rm -rf /"$bootfs" >& /dev/null
cd /"$bootfs"
# Mount the Triton USB key (even if it's a virtual one...).
if [[ $(sdc-usbkey status) == "mounted" ]]; then
stick_premounted=yes
fi
stickmount=$(sdc-usbkey mount)
vecho "Mounted USB key on $stickmount"
# NOTE: BAIL ON VERSION 1 STICKS FOR NOW
version=$(sdc-usbkey status -j | json version)
if [[ "$version" != "2" ]]; then
# Unmount on version-mismatch...
usb_unmount_unless_already
err "USB key must be Version 2 (loader) to install on a pool."
fi
# Copy over the whole thing to ${bootfs}
vecho "Copying over USB key contents to /$bootfs"
# NOTE: If failed here, USB key will still be mounted for debugging
# reasons, regardless of $stick_premounted.
tar -cf - -C "$stickmount" . | tar -xf - || \
err "Problem copying USB key on $stickmount (still mounted)" \
"to /$bootfs"
# Add both fstype="ufs" to loader.conf.
vecho "Modifying loader.conf for pool-based Triton Head Node boot"
grep -q 'fstype="ufs"' ./boot/loader.conf || \
echo 'fstype="ufs"' >> ./boot/loader.conf
# Filter out any old triton_ bootparams (either old bootpool OR
# installer properties) and replace them with JUST the new bootpool.
# Let's be cautious and use a temp file instead of sed's -i or -I.
tfile=$(mktemp)
grep -v '^triton_' ./boot/loader.conf > $tfile
mv -f $tfile ./boot/loader.conf
echo "triton_bootpool=\"$pool\"" >> ./boot/loader.conf
# (OPTIONAL) Add "from-pool" indicator to boot.4th.
# 4.) Properly capitalize ${bootfs}/os/ entries.
vecho "Case-correcting os/ entries."
cd ./os
for a in *; do
# If our "usb key" is already a ZFS bootfs, don't mv
# identically-named things.
if [[ "$a" != "${a^^}" ]]; then
mv "$a" "${a^^}"
fi
done
cd ..
if [[ "$stick_premounted" != "yes" ]]; then
sdc-usbkey unmount
fi
}
enablepool() {
if [[ $1 == "-i" ]]; then
if [[ "$2" == "" || "$3" == "" ]]; then
eecho "-i must take an option," \
"and then a pool must be specified."
usage
fi
standalone_only "'bootable -e' with '-i'"
installsource=$2
pool=$3
elif [[ -z $1 ]]; then
eecho "To enable a pool for booting, please specify at least" \
"a pool"
usage
else
installsource="media"
pool=$1
fi
# SmartOS standard bootable filesystem is POOL/boot.
bootfs=${pool}/boot
# If we're a head node, bail early if we don't have a sufficiently
# advanced set of gz-tools.
if [[ "$TRITON_HN" == "yes" && ! -f /opt/smartdc/lib/bootpool.js ]]
then
eecho ""
eecho "To activate a pool for Triton head node booting, newer"
eecho "global-zone tools (namely support for sdc-usbkey to"
eecho "treat $bootfs as a USB key equivalent) are required."
eecho ""
eecho "Please update your headnode global-zone tools by"
err "using 'sdcadm experimental update-gz-tools' and try again."
fi
if ispoolenabled "$pool" ; then
# NOTE: Different actions depending on standalone, CN, or HN.
if [[ -d /${bootfs}/platform/. && -d /${bootfs}/boot/. ]]; then
echo "Pool $pool appears to be bootable."
if [[ "$TRITON_CN" == "yes" ]]; then
echo "Use 'piadm update' to update the CN's" \
"iPXE and backup PI."
elif [[ "$TRITON_HN" == "yes" ]]; then
echo "Use 'sdcadm platform' to change PIs."
else
echo "Use 'piadm install' and" \
"'piadm activate' to change PIs."
fi
return 0
fi
# One or both of "platform" or "boot" aren't there.
# For now, proceed clobber-style.
fi
if ! zfs list -H "$bootfs" > /dev/null 2>&1; then
# Create a new bootfs and set it.
# NOTE: Encryption should be turned off for this dataset.
zfs create -o encryption=off "$bootfs" || \
fatal "Cannot create $bootfs dataset"
fi
# We MAY need to do some reality checking if the `zfs list` shows
# $bootfs. For now, just wing it. and plow forward.
# At this point we have an existing SmartOS-standard boot filesystem,
# but it's not specified as bootfs in the pool. Test if bootfs can be
# set...
zpool set "bootfs=${bootfs}" "${pool}" || \
fatal "Cannot set bootfs for $pool"
# Reset our view of available bootable pools.
getbootable
if [[ "$TRITON_CN" == "yes" ]]; then
bringup_CN
update_boot_sectors "$pool" "$bootfs"
elif [[ "$TRITON_HN" == "yes" ]]; then
bringup_HN
update_boot_sectors "$pool" "$bootfs"
echo "NOTE: Directory $bootfs on pool $pool is now able to be"
echo "this head node's virtual bootable USB key."
echo ""
echo "If this isn't a replacement pool for an existing bootable"
echo "pool, you should remove the USB key from this headnode"
echo "and then reboot this headnode from a disk in $pool"
echo ""
echo "The USB key (even if it's another pool's bootfs) is"
echo "now" $(sdc-usbkey status) "because that is what it was"
echo "before piadm ran."
echo ""
else
install "$installsource" "$pool"
# install set 'installstamp' on our behalf.
activate "$installstamp" "$pool"
fi
}
refresh_or_disable_pool() {
flag=$1
pool=$2
if [[ -z $pool ]]; then
eecho "Must specify a pool for disabling or refresh"
usage
fi
currbootfs=""
# ispoolenabled sets currbootfs as a side-effect.
ispoolenabled "$pool" || \
err "Pool $pool is not bootable, and cannot be disabled or refreshed"
if [[ "$flag" == "-d" ]]; then
if [[ "$TRITON_HN" == "yes" ]]; then
if [[ "$pool" == "$TRITON_HN_BOOTPOOL" ]]; then
# The warning says it all.
eecho "WARNING: Disabling currently-booting" \
"pool"
# For now, just bail in this case.
err "Please boot from a USB key or other pool"
# TODO? -- Check to see if there
# are alternatives before disabling?
#
# First, use this tool to find other
# bootable pools.
#
# echo "Other available pools:"
# echo ""
# bootable | grep -v "$pool"
# echo ""
#
# Then check to see if there's an
# actual USB key available.
#
# tdir=$(mktemp -d)
# ... use new -u (force USB key search) option
# sdc-usbkey mount -u $tdir
# ... if tdir has a usb key, mention it
# sdc-usbkey unmount $tdir
else
vecho "Disabling Triton Head Node inactive" \
"bootable pool."
fi
fi
vecho "Disabling bootfs on pool $pool"
zpool set bootfs="" "$pool"
else
vecho "Refreshing boot sectors and/or ESP on pool $pool"
# Refreshing works the same for all of standalone, compute
# node, or head node.
fi
update_boot_sectors "$pool" "$currbootfs" "$flag"
return 0
}
bootable() {
if [[ $1 == "-d" || "$1" == "-r" ]]; then
refresh_or_disable_pool "$1" "$2"
return
elif [[ $1 == "-e" ]]; then
shift 1
enablepool "$@"
return
fi
# If we reach here, we're querying about a pool.
if [[ "$1" == "" ]]; then
mapfile -t allpools < <(zpool list -Ho name)
else
# Reality check for bad pool name.
poolpresent "$1"
# Or have a list of one pool...
allpools=( "$1" )
fi
# We're guaranteed that, modulo background processes, $allpools has a
# list of actual pools, even if it's a list-of-one.
for pool in "${allpools[@]}"; do
if zpool list -Ho bootfs "$pool" | grep -q "${pool}/boot" ; then
bootable="BIOS"
# Check for pcfs partition on pool disks.
mapfile -t boot_devices < \
<(zpool list -vHP "${pool}" | \
grep -E 'c[0-9]+' | awk '{print $1}')
for a in "${boot_devices[@]}"; do
noslice=$(echo "$a" | sed -E 's/s[0-9]+//g')
tdir=$(mktemp -d)
# Assume that s0 on the physical disk would be
# where the EFI System Partition (ESP) lives.
# A pcfs mount, ALONG WITH a check for a
# bootx64.efi executable, can confirm/deny
# it. Do this instead of just checking for
# bootsize because we can further
# integrity-check here if need be.
if mount -F pcfs "/dev/dsk/${noslice}s0" "$tdir" \
> /dev/null 2>&1 && \
[[ -f "$tdir/EFI/Boot/bootx64.efi" ]]; then
efi="and UEFI"
else
efi=""
fi
umount -f "$tdir" > /dev/null 2>&1 && rmdir "$tdir"
done
else
bootable="non-bootable"
efi=""
fi
printf "%-30s ==> %s %s\n" "$pool" "$bootable" "$efi"
done
}
if [[ "$1" == "-v" ]]; then
VERBOSE=1
shift 1
elif [[ "$1" == "-vv" ]]; then
set -x
VERBOSE=1
shift 1
else
VERBOSE=0
fi
# Determine if we're running on a Triton Compute Node (CN) or not:
bootparams | grep -E -q 'smartos=|headnode=' || initialize_as_CN
bootparams | grep -q 'headnode=' && initialize_as_HN
# Check the configuration file out.
config_check
cmd=$1
shift 1
case $cmd in
activate | assign )
privcheck "$cmd"
standalone_only "$cmd"
activate "$@"
;;
avail )
standalone_only avail
avail
;;
bootable )
privcheck bootable
bootable "$@"
;;
install )
privcheck install
standalone_only install
install "$@"
cd /${bootfs}
regenerate_os
;;
list )
list "$@"
;;
remove )
privcheck remove
standalone_only remove
remove "$@"
;;
update )
privcheck update
update_CN "$@"
;;
*)
usage
;;
esac
exit 0