#!/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 2020 Joyent, Inc. # set -o xtrace fatal() { echo "Error: $1" exit $SMF_EXIT_ERR_FATAL } . /lib/svc/share/smf_include.sh . /lib/svc/share/fs_include.sh . /lib/sdc/usb-key.sh # first of all, if we aren't the global zone this doesn't make any sense to run smf_is_globalzone || exit $SMF_EXIT_OK # We need the links to /dev/dsk. Rather than trying to play games with manually # invoking syseventd ask devfsadm to do some work. /usr/sbin/devfsadm -c disk function destroy_zpools { for pool in $(zpool list -p -o name | grep -v NAME) ; do zpool destroy -f ${pool} done } function mount_zfs { local dataset=$1 local mountpoint=$2 local output= # # Try to mount the ZFS dataset. If the mountpoint is busy, wait five # seconds and try again. Fail if the mount attempt returns EBUSY three # consecutive times. # for i in {1..3}; do output=$(mount -F zfs ${dataset} ${mountpoint} 2>&1) if [[ $? -eq 0 ]]; then break fi if [ "${output}" == "mount failed: Device busy" ]; then sleep 5 else echo ${output} 1>&2 return fi done # The mount attempt must have failed echo ${output} 1>&2 } function unlock_pool { local pool=$1 # If the key is already loaded, don't bother trying again local keystatus="$(zfs get -Hpo value keystatus $pool)" if [[ "$keystatus" == "available" ]]; then return fi kbmadm unlock $pool && return echo "Failed to unlock $pool; recovery may be required" | \ tee -a /dev/console >&2 exit $SMF_EXIT_ERR_FATAL } /bin/bootparams | grep "^noimport=true" >/dev/null if [ $? -ne 0 ]; then # If the zpool doesn't exist, then there's nothing to mount. # Assume the system zpool is zones, but if a different system pool # identifies itself (by virtue of the .system_pool file being present in the # pool's root dataset), then use that system pool instead. SYS_ZPOOL=zones # Import specified zpools, or all zpools available pools=$(/bin/bootparams | egrep "^zpools?=" | cut -d= -f2 | tr , ' ') if [ -z ${pools} ]; then pools=$(zpool import | grep "pool:" | awk '{print $2}') fi for pool in $pools; do zpool import -f $pool || continue is_encr="$(zfs get -Hpo value encryption $pool)" [[ "$is_encr" != "off" ]] && unlock_pool $pool # Due to early, failed attempts to support the filesystem_limits # feature we now need to ensure the dependent feature is enabled. zpool set feature@extensible_dataset=enabled $pool if [[ -f /$pool/.system_pool ]]; then SYS_ZPOOL=$pool [[ "$is_encr" != "off" ]] && kbmadm set-syspool $pool fi done svccfg -s svc:/system/smartdc/init setprop \ config/zpool=${SYS_ZPOOL} svccfg -s svc:/system/smartdc/init:default refresh # If the destroy_zpools boot parameter is set, destroy all zpools /bin/bootparams | grep "^destroy_zpools=true" >/dev/null if [ $? -eq 0 ]; then destroy_zpools fi # A machine is reset to its original unsetup state (i.e. a 'factory reset') # when the smartdc:factoryreset ZFS user property is set on the var dataset. reset=$(zfs get -H -o value smartdc:factoryreset ${SYS_ZPOOL}/var) if [ "${reset}" == "yes" ]; then destroy_zpools fi # Capture the zpool's status output in the method's log file for # troubleshooting. # # Note: It is critical that we do not run 'status -v'. If there are errors # in the zpool error log and the zpool is large (e.g. > 200TB), then the # lookup for the error file names can take a very long time (several hours). # This would block the system boot until it completed. zpool status ${SYS_ZPOOL} if [ $? -eq 0 ]; then # Stash the SUNWdefault.xml file so we can update the # persistent version after mounting zones/config. cp /etc/zones/SUNWdefault.xml /tmp/ # Mount and configure all system datasets mount_zfs ${SYS_ZPOOL}/var /var mount_zfs ${SYS_ZPOOL}/config /etc/zones mount_zfs ${SYS_ZPOOL}/opt /opt # Update the the persistent SUNWdefault.xml file to match the # contents on ramdisk now that zones/config is mounted. cp /tmp/SUNWdefault.xml /etc/zones/ rm -f /tmp/SUNWdefault.xml # # We include a manifest of all files shipped in the platform image, # along with an MD5 hash of their contents. This was originally # shipped as "/var/log/manifest", but once a machine is set up, "/var" # now comes from the pool. The upshot of this is that every SmartOS # machine has the manifest from the platform at setup time stored in # "/var/log/manifest". Now that the manifest has moved to an # accessible location, we should remove this file and replace it with a # symbolic link. # if [[ -f '/var/log/manifest' && ! -L '/var/log/manifest' && ! -e '/var/log/manifest.original' ]]; then mv '/var/log/manifest' '/var/log/manifest.original' ln -s '../../usr/share/smartos/manifest' '/var/log/manifest' fi if [[ -z $(/bin/bootparams | grep '^smartos=true') ]]; then mkdir -p /opt/smartdc/agents/smf mount -O -F lofs /var/svc/manifest/site /opt/smartdc/agents/smf fi if [[ -n $(/bin/bootparams | grep '^headnode=true') || \ -n $(/bin/bootparams | grep '^smartos=true') ]]; then mkdir /usbkey mount_zfs ${SYS_ZPOOL}/usbkey /usbkey fi if [[ -n $(/bin/bootparams | grep '^smartos=true') ]]; then mount -F lofs /usbkey/shadow /etc/shadow mount -F lofs /usbkey/ssh /etc/ssh fi swap -a /dev/zvol/dsk/${SYS_ZPOOL}/swap || \ fatal "failed to configure swap device" # # Configure the dump device on top of a ZFS volume. In addition to the # usual dumpadm(1m) call, there are two prerequisites for using this # volume as a dump device: (1) that zvol must be using the noparity # checksum algorithem, and (2) the MULTI_VDEV_CRASH_DUMP ZFS feature # must be enabled. Prerequisite (1) is necessary since the exact # on-disk value for ZIO_CHECKSUM_NOPARITY has changed, so to avoid a # flag day on all systems, this service just sets that property again # every time. # zfs set checksum=noparity ${SYS_ZPOOL}/dump || \ fatal "failed to set checksum=noparity on dump zvol" zpool set feature@multi_vdev_crash_dump=enabled ${SYS_ZPOOL} || \ fatal "failed to enable multi_vdev_crash_dump ZFS feature" dumpadm -y -d /dev/zvol/dsk/${SYS_ZPOOL}/dump || \ fatal "failed to configure dump device" zfs list -H -o name ${SYS_ZPOOL}/cores/global >/dev/null 2>&1 if [ $? -ne 0 ]; then # Booting for the first time on a CN whose cores dataset is setup # in the 6.x style. Convert to the new style. zfs destroy -r ${SYS_ZPOOL}/cores zfs create -o compression=gzip -o mountpoint=none ${SYS_ZPOOL}/cores zfs create -o quota=10g -o mountpoint=/${SYS_ZPOOL}/global/cores \ ${SYS_ZPOOL}/cores/global fi ln -s /${SYS_ZPOOL}/global/cores /cores [[ -f /${SYS_ZPOOL}/currbooted ]] && \ mv /${SYS_ZPOOL}/currbooted /${SYS_ZPOOL}/lastbooted uname -v >/${SYS_ZPOOL}/currbooted fi fi # The rest only applies to the headnode /bin/bootparams | grep "^headnode=true" >/dev/null || exit $SMF_EXIT_OK # If we rebooted during an upgrade, we're in deep trouble. if [ -d /var/upgrade_in_progress ]; then echo "ERROR: An upgrade was in progress when the system rebooted." \ >/dev/console echo " The system is in an indeterminate state, unable to continue." \ >/dev/console exit $SMF_EXIT_ERR_FATAL fi COPYINPOINT=`svcprop -p "joyentfs/usb_copy_path" ${SMF_FMRI}` DEBUG=`svcprop -p "joyentfs/debug" ${SMF_FMRI}` if [[ -d /mnt ]]; then chown root:root /mnt chmod 700 /mnt else mkdir -m 700 /mnt fi function make_usb_copy_if_possible { [[ -n "${SYS_ZPOOL}" ]] || fatal "don't know system zpool name" zpool list -Ho name | grep "^${SYS_ZPOOL}\$" if [[ $? != 0 ]]; then echo "skipping USB copy setup: no ${SYS_ZPOOL} zpool" >/dev/console # Still return OK, because this is the expected case for first headnode # boot. return 0 fi USBDATASET=${SYS_ZPOOL}/usbkey if ! zfs list -Ho name | grep "^${USBDATASET}\$" >/dev/null; then echo "skipping USB copy setup: no zones/usbkey dataset" >/dev/console # Still return OK, because as of HEAD-2343 a CN being converted to a HN # will not yet have this dataset on its first boot as an HN. return 0 fi echo "" > /dev/console echo "Moving files from USB boot device onto disk storage." > /dev/console echo "This may take several minutes. Please note the time..." > /dev/console echo "" > /dev/console echo "" > /dev/console mkdir ${COPYINPOINT} mount_zfs ${USBDATASET} ${COPYINPOINT} (cd ${USBMOUNTPOINT}; rsync -av --log-file=/dev/console --exclude private --exclude os * ${COPYINPOINT}) if [[ -d ${USBMOUNTPOINT}/os ]]; then (cd ${USBMOUNTPOINT}/os ; \ for dir in $(ls -d *); do # source comes from pcfs which we've got lowering the case # of everything, but we normally use capital T and Z for # buildstamp, so fix it here. source_dir=${dir} target_dir=$(echo ${dir} | tr "[:lower:]" "[:upper:]") mkdir -p ${COPYINPOINT}/os echo "Copying: ${source_dir}/ ${COPYINPOINT}/os/${target_dir}" > /dev/console rsync -a ${source_dir}/ ${COPYINPOINT}/os/${target_dir} done ) fi echo "" > /dev/console echo "Done copying files from USB device" > /dev/console return 0 } USBMOUNTPOINT=$(mount_usb_key "") if [[ $? -ne 0 ]]; then fatal "couldn't mount USB key" fi make_usb_copy_if_possible exit $?