functions.sh revision 214139
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: head/usr.sbin/pc-sysinstall/backend/functions.sh 214139 2010-10-21 17:20:37Z imp $
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
328write_image()
329{
330  local DEVICE_FILE
331
332  IMAGE_FILE="$1"
333  DEVICE_FILE="$2"
334
335  if [ -z "${IMAGE_FILE}" ]
336  then
337    exit_err "ERROR: Image file not specified!"
338  fi
339 
340  if [ -z "${DEVICE_FILE}" ]
341  then
342    exit_err "ERROR: Device file not specified!"
343  fi
344 
345  if [ ! -f "${IMAGE_FILE}" ]
346  then
347    exit_err "ERROR: '${IMAGE_FILE}' does not exist!"
348  fi
349
350  DEVICE_FILE="${DEVICE_FILE#/dev/}"
351  DEVICE_FILE="/dev/${DEVICE_FILE}"
352 
353  if [ ! -c "${DEVICE_FILE}" ]
354  then
355    exit_err "ERROR: '${DEVICE_FILE}' is not a character device!"
356  fi
357
358  if iscompressed "${IMAGE_FILE}"
359  then
360	local COMPRESSION
361
362    get_compression_type "${IMAGE_FILE}"
363	COMPRESSION="${VAL}"
364
365    case "${COMPRESSION}" in
366      lzw)
367        rc_halt "uncompress ${IMAGE_FILE} -c | dd of=${DEVICE_FILE} bs=128k"
368        IMAGE_FILE="${IMAGE_FILE%.Z}"
369        ;;
370
371      lzo)
372        rc_halt "lzop -d $IMAGE_{FILE} -c | dd of=${DEVICE_FILE} bs=128k"
373        IMAGE_FILE="${IMAGE_FILE%.lzo}"
374        ;;
375
376      lzma)
377        rc_halt "lzma -d ${IMAGE_FILE} -c | dd of=${DEVICE_FILE} bs=128k"
378        IMAGE_FILE="${IMAGE_FILE%.lzma}"
379        ;;
380
381      gzip)
382        rc_halt "gunzip ${IMAGE_FILE} -c | dd of=${DEVICE_FILE} bs=128k"
383        IMAGE_FILE="${IMAGE_FILE%.gz}"
384        ;;
385
386      bzip2)
387        rc_halt "bunzip2 ${IMAGE_FILE} -c | dd of=${DEVICE_FILE} bs=128k"
388        IMAGE_FILE="${IMAGE_FILE%.bz2}"
389        ;;
390
391      xz)
392        rc_halt "xz -d ${IMAGE_FILE} -c | dd of=${DEVICE_FILE} bs=128k"
393        IMAGE_FILE="${IMAGE_FILE%.xz}"
394        ;;
395
396      zip)
397        rc_halt "unzip ${IMAGE_FILE} -c | dd of=${DEVICE_FILE} bs=128k"
398        IMAGE_FILE="${IMAGE_FILE%.zip}"
399        ;;
400
401      *) 
402        exit_err "ERROR: ${COMPRESSION} compression is not supported"
403        ;;
404    esac
405
406  else
407    rc_halt "dd if=${IMAGE_FILE} of=${DEVICE_FILE} bs=128k"
408
409  fi
410};
411
412install_fresh()
413{
414  # Lets start setting up the disk slices now
415  setup_disk_slice
416  
417  if [ -z "${ROOTIMAGE}" ]
418  then
419
420    # Disk setup complete, now lets parse WORKINGSLICES and setup the bsdlabels
421    setup_disk_label
422  
423    # Now we've setup the bsdlabels, lets go ahead and run newfs / zfs 
424    # to setup the filesystems
425    setup_filesystems
426
427    # Lets mount the partitions now
428    mount_all_filesystems
429
430    # We are ready to begin extraction, lets start now
431    init_extraction 
432
433    # Check if we have any optional modules to load 
434    install_components
435
436    # Check if we have any packages to install
437    install_packages
438
439    # Do any localization in configuration
440    run_localize
441  
442    # Save any networking config on the installed system
443    save_networking_install
444
445    # Now add any users
446    setup_users
447
448    # Now run any commands specified
449    run_commands
450  
451    # Do any last cleanup / setup before unmounting
452    run_final_cleanup
453
454    # Unmount and finish up
455    unmount_all_filesystems
456  fi
457
458  echo_log "Installation finished!"
459};
460
461install_image()
462{
463  # We are ready to begin extraction, lets start now
464  init_extraction 
465
466  echo_log "Installation finished!"
467};
468
469install_upgrade()
470{
471  # We're going to do an upgrade, skip all the disk setup 
472  # and start by mounting the target drive/slices
473  mount_upgrade
474  
475  # Start the extraction process
476  init_extraction
477
478  # Do any localization in configuration
479  run_localize
480
481  # ow run any commands specified
482  run_commands
483  
484  # Merge any old configuration files
485  merge_old_configs
486
487  # Check if we have any optional modules to load 
488  install_components
489
490  # Check if we have any packages to install
491  install_packages
492
493  # All finished, unmount the file-systems
494  unmount_upgrade
495
496  echo_log "Upgrade finished!"
497};
498