1#!/bin/sh 2#- 3# SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4# 5# Copyright (c) 2010 iXsystems, Inc. All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28# $FreeBSD$ 29 30# functions.sh 31# Library of functions which pc-sysinstall may call upon 32 33# Function which displays the help-index file 34display_help() 35{ 36 if [ -e "${PROGDIR}/doc/help-index" ] 37 then 38 cat ${PROGDIR}/doc/help-index 39 else 40 echo "Error: ${PROGDIR}/doc/help-index not found" 41 exit 1 42 fi 43}; 44 45# Function which displays the help for a specified command 46display_command_help() 47{ 48 if [ -z "$1" ] 49 then 50 echo "Error: No command specified to display help for" 51 exit 1 52 fi 53 54 if [ -e "${PROGDIR}/doc/help-${1}" ] 55 then 56 cat ${PROGDIR}/doc/help-${1} 57 else 58 echo "Error: ${PROGDIR}/doc/help-${1} not found" 59 exit 1 60 fi 61}; 62 63# Function to convert bytes to megabytes 64convert_byte_to_megabyte() 65{ 66 if [ -z "${1}" ] 67 then 68 echo "Error: No bytes specified!" 69 exit 1 70 fi 71 72 expr -e ${1} / 1048576 73}; 74 75# Function to convert blocks to megabytes 76convert_blocks_to_megabyte() 77{ 78 if [ -z "${1}" ] ; then 79 echo "Error: No blocks specified!" 80 exit 1 81 fi 82 83 expr -e ${1} / 2048 84}; 85 86# Takes $1 and strips the whitespace out of it, returns VAL 87strip_white_space() 88{ 89 if [ -z "${1}" ] 90 then 91 echo "Error: No value setup to strip whitespace from!" 92 93 exit 1 94 fi 95 96 export VAL=`echo "$1" | tr -d ' '` 97}; 98 99# Displays an error message and exits with error 1 100exit_err() 101{ 102 # Echo the message for the users benefit 103 echo "EXITERROR: $1" 104 105 # Save this error to the log file 106 echo "EXITERROR: ${1}" >>$LOGOUT 107 108 # Check if we need to unmount any file-systems after this failure 109 unmount_all_filesystems_failure 110 111 echo "For more details see log file: $LOGOUT" 112 113 exit 1 114}; 115 116# Run-command, don't halt if command exits with non-0 117rc_nohalt() 118{ 119 CMD="$1" 120 121 if [ -z "${CMD}" ] 122 then 123 exit_err "Error: missing argument in rc_nohalt()" 124 fi 125 126 echo "Running: ${CMD}" >>${LOGOUT} 127 ${CMD} >>${LOGOUT} 2>>${LOGOUT} 128 129}; 130 131# Run-command, halt if command exits with non-0 132rc_halt() 133{ 134 CMD="$1" 135 136 if [ -z "${CMD}" ] 137 then 138 exit_err "Error: missing argument in rc_halt()" 139 fi 140 141 echo "Running: ${CMD}" >>${LOGOUT} 142 eval ${CMD} >>${LOGOUT} 2>>${LOGOUT} 143 STATUS="$?" 144 if [ "${STATUS}" != "0" ] 145 then 146 exit_err "Error ${STATUS}: ${CMD}" 147 fi 148}; 149 150# Run-command w/echo to screen, halt if command exits with non-0 151rc_halt_echo() 152{ 153 CMD="$1" 154 155 if [ -z "${CMD}" ] 156 then 157 exit_err "Error: missing argument in rc_halt_echo()" 158 fi 159 160 echo "Running: ${CMD}" >>${LOGOUT} 161 ${CMD} 2>&1 | tee -a ${LOGOUT} 162 STATUS="$?" 163 if [ "$STATUS" != "0" ] 164 then 165 exit_err "Error ${STATUS}: $CMD" 166 fi 167 168}; 169 170# Run-command w/echo, don't halt if command exits with non-0 171rc_nohalt_echo() 172{ 173 CMD="$1" 174 175 if [ -z "${CMD}" ] 176 then 177 exit_err "Error: missing argument in rc_nohalt_echo()" 178 fi 179 180 echo "Running: ${CMD}" >>${LOGOUT} 181 ${CMD} 2>&1 | tee -a ${LOGOUT} 182 183}; 184 185# Echo to the screen and to the log 186echo_log() 187{ 188 STR="$1" 189 190 if [ -z "${STR}" ] 191 then 192 exit_err "Error: missing argument in echo_log()" 193 fi 194 195 echo "${STR}" | tee -a ${LOGOUT} 196}; 197 198# Make sure we have a numeric 199is_num() 200{ 201 expr $1 + 1 2>/dev/null 202 return $? 203} 204 205# Function which uses "fetch" to download a file, and display a progress report 206fetch_file() 207{ 208 209 FETCHFILE="$1" 210 FETCHOUTFILE="$2" 211 EXITFAILED="$3" 212 213 EXITFILE="${TMPDIR}/.fetchExit" 214 215 rm ${FETCHOUTFILE} 2>/dev/null >/dev/null 216 217 SIZE=$(( `fetch -s "${FETCHFILE}"` / 1024 )) 218 echo "FETCH: ${FETCHFILE}" 219 echo "FETCH: ${FETCHOUTFILE}" >>${LOGOUT} 220 221 ( fetch -o ${FETCHOUTFILE} "${FETCHFILE}" >/dev/null 2>/dev/null ; echo "$?" > ${EXITFILE} ) & 222 PID="$!" 223 while 224 z=1 225 do 226 227 if [ -e "${FETCHOUTFILE}" ] 228 then 229 DSIZE=`du -k ${FETCHOUTFILE} | tr -d '\t' | cut -d '/' -f 1` 230 if [ $(is_num "$DSIZE") ] ; then 231 if [ $SIZE -lt $DSIZE ] ; then DSIZE="$SIZE"; fi 232 echo "SIZE: ${SIZE} DOWNLOADED: ${DSIZE}" 233 echo "SIZE: ${SIZE} DOWNLOADED: ${DSIZE}" >>${LOGOUT} 234 fi 235 fi 236 237 # Check if the download is finished 238 ps -p ${PID} >/dev/null 2>/dev/null 239 if [ $? -ne 0 ] 240 then 241 break; 242 fi 243 244 sleep 2 245 done 246 247 echo "FETCHDONE" 248 249 EXIT="`cat ${EXITFILE}`" 250 if [ "${EXIT}" != "0" -a "$EXITFAILED" = "1" ] 251 then 252 exit_err "Error: Failed to download ${FETCHFILE}" 253 fi 254 255 return $EXIT 256 257}; 258 259# Function to return a the zpool name for this device 260get_zpool_name() 261{ 262 DEVICE="$1" 263 264 # Set the base name we use for zpools 265 BASENAME="tank" 266 267 if [ ! -d "${TMPDIR}/.zpools" ] ; then 268 mkdir -p ${TMPDIR}/.zpools 269 fi 270 271 if [ -e "${TMPDIR}/.zpools/${DEVICE}" ] ; then 272 cat ${TMPDIR}/.zpools/${DEVICE} 273 return 0 274 else 275 # Need to generate a zpool name for this device 276 NUM=`ls ${TMPDIR}/.zpools/ | wc -l | sed 's| ||g'` 277 278 # Is it used in another zpool? 279 while : 280 do 281 NEWNAME="${BASENAME}${NUM}" 282 zpool list | grep -qw "${NEWNAME}" 283 local chk1=$? 284 zpool import | grep -qw "${NEWNAME}" 285 local chk2=$? 286 if [ $chk1 -eq 1 -a $chk2 -eq 1 ] ; then break ; fi 287 NUM=$((NUM+1)) 288 done 289 290 # Now save the new tank name 291 mkdir -p ${TMPDIR}/.zpools/`dirname $DEVICE` 292 echo "$NEWNAME" >${TMPDIR}/.zpools/${DEVICE} 293 echo "${NEWNAME}" 294 return 0 295 fi 296}; 297 298iscompressed() 299{ 300 local FILE 301 local RES 302 303 FILE="$1" 304 RES=1 305 306 if echo "${FILE}" | \ 307 grep -qiE '\.(Z|lzo|lzw|lzma|gz|bz2|xz|zip)$' 2>&1 308 then 309 RES=0 310 fi 311 312 return ${RES} 313} 314 315get_compression_type() 316{ 317 local FILE 318 local SUFFIX 319 320 FILE="$1" 321 SUFFIX=`echo "${FILE}" | sed -E 's|^(.+)\.(.+)$|\2|'` 322 323 VAL="" 324 SUFFIX=`echo "${SUFFIX}" | tr A-Z a-z` 325 case "${SUFFIX}" in 326 z) VAL="lzw" ;; 327 lzo) VAL="lzo" ;; 328 lzw) VAL="lzw" ;; 329 lzma) VAL="lzma" ;; 330 gz) VAL="gzip" ;; 331 bz2) VAL="bzip2" ;; 332 xz) VAL="xz" ;; 333 zip) VAL="zip" ;; 334 esac 335 336 export VAL 337} 338 339write_image() 340{ 341 local DEVICE_FILE 342 343 IMAGE_FILE="$1" 344 DEVICE_FILE="$2" 345 346 if [ -z "${IMAGE_FILE}" ] 347 then 348 exit_err "ERROR: Image file not specified!" 349 fi 350 351 if [ -z "${DEVICE_FILE}" ] 352 then 353 exit_err "ERROR: Device file not specified!" 354 fi 355 356 if [ ! -f "${IMAGE_FILE}" ] 357 then 358 exit_err "ERROR: '${IMAGE_FILE}' does not exist!" 359 fi 360 361 DEVICE_FILE="${DEVICE_FILE#/dev/}" 362 DEVICE_FILE="/dev/${DEVICE_FILE}" 363 364 if [ ! -c "${DEVICE_FILE}" ] 365 then 366 exit_err "ERROR: '${DEVICE_FILE}' is not a character device!" 367 fi 368 369 if iscompressed "${IMAGE_FILE}" 370 then 371 local COMPRESSION 372 373 get_compression_type "${IMAGE_FILE}" 374 COMPRESSION="${VAL}" 375 376 case "${COMPRESSION}" in 377 lzw) 378 rc_halt "uncompress ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}" 379 IMAGE_FILE="${IMAGE_FILE%.Z}" 380 ;; 381 382 lzo) 383 rc_halt "lzop -d $IMAGE_{FILE} -c | dd of=${DEVICE_FILE}" 384 IMAGE_FILE="${IMAGE_FILE%.lzo}" 385 ;; 386 387 lzma) 388 rc_halt "lzma -d ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}" 389 IMAGE_FILE="${IMAGE_FILE%.lzma}" 390 ;; 391 392 gzip) 393 rc_halt "gunzip ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}" 394 IMAGE_FILE="${IMAGE_FILE%.gz}" 395 ;; 396 397 bzip2) 398 rc_halt "bunzip2 ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}" 399 IMAGE_FILE="${IMAGE_FILE%.bz2}" 400 ;; 401 402 xz) 403 rc_halt "xz -d ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}" 404 IMAGE_FILE="${IMAGE_FILE%.xz}" 405 ;; 406 407 zip) 408 rc_halt "unzip ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}" 409 IMAGE_FILE="${IMAGE_FILE%.zip}" 410 ;; 411 412 *) 413 exit_err "ERROR: ${COMPRESSION} compression is not supported" 414 ;; 415 esac 416 417 else 418 rc_halt "dd if=${IMAGE_FILE} of=${DEVICE_FILE}" 419 420 fi 421}; 422 423# Setup and install on a new disk / partition 424install_fresh() 425{ 426 # Lets start setting up the disk slices now 427 setup_disk_slice 428 429 if [ -z "${ROOTIMAGE}" ] 430 then 431 432 # Disk setup complete, now lets parse WORKINGSLICES and setup the bsdlabels 433 setup_disk_label 434 435 # Now we've setup the bsdlabels, lets go ahead and run newfs / zfs 436 # to setup the filesystems 437 setup_filesystems 438 439 # Lets mount the partitions now 440 mount_all_filesystems 441 442 # We are ready to begin extraction, lets start now 443 init_extraction 444 445 # Check if we have any optional modules to load 446 install_components 447 448 # Check if we have any packages to install 449 install_packages 450 451 # Do any localization in configuration 452 run_localize 453 454 # Save any networking config on the installed system 455 save_networking_install 456 457 # Now add any users 458 setup_users 459 460 # Do any last cleanup / setup before unmounting 461 run_final_cleanup 462 463 # Now run any commands specified 464 run_commands 465 466 # Unmount and finish up 467 unmount_all_filesystems 468 fi 469 470 echo_log "Installation finished!" 471}; 472 473# Extract the system to a pre-mounted directory 474install_extractonly() 475{ 476 # We are ready to begin extraction, lets start now 477 init_extraction 478 479 # Check if we have any optional modules to load 480 install_components 481 482 # Check if we have any packages to install 483 install_packages 484 485 # Do any localization in configuration 486 run_localize 487 488 # Save any networking config on the installed system 489 save_networking_install 490 491 # Now add any users 492 setup_users 493 494 # Now run any commands specified 495 run_commands 496 497 # Set a hostname on the install system 498 setup_hostname 499 500 # Set the root_pw if it is specified 501 set_root_pw 502 503 echo_log "Installation finished!" 504}; 505 506install_image() 507{ 508 # We are ready to begin extraction, lets start now 509 init_extraction 510 511 echo_log "Installation finished!" 512}; 513 514install_upgrade() 515{ 516 # We're going to do an upgrade, skip all the disk setup 517 # and start by mounting the target drive/slices 518 mount_upgrade 519 520 # Start the extraction process 521 init_extraction 522 523 # Do any localization in configuration 524 run_localize 525 526 # Now run any commands specified 527 run_commands 528 529 # Merge any old configuration files 530 merge_old_configs 531 532 # Check if we have any optional modules to load 533 install_components 534 535 # Check if we have any packages to install 536 install_packages 537 538 # All finished, unmount the file-systems 539 unmount_upgrade 540 541 echo_log "Upgrade finished!" 542}; 543