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