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: releng/10.3/usr.sbin/pc-sysinstall/backend/functions.sh 247735 2013-03-03 23:07:27Z jpaetzel $
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  export VAL=`echo "$1" | tr -d ' '`
95};
96
97# Displays an error message and exits with error 1
98exit_err()
99{
100  # Echo the message for the users benefit
101  echo "EXITERROR: $1"
102
103  # Save this error to the log file
104  echo "EXITERROR: ${1}" >>$LOGOUT
105
106  # Check if we need to unmount any file-systems after this failure
107  unmount_all_filesystems_failure
108
109  echo "For more details see log file: $LOGOUT"
110
111  exit 1
112};
113
114# Run-command, don't halt if command exits with non-0
115rc_nohalt()
116{
117  CMD="$1"
118
119  if [ -z "${CMD}" ]
120  then
121    exit_err "Error: missing argument in rc_nohalt()"
122  fi
123
124  echo "Running: ${CMD}" >>${LOGOUT}
125  ${CMD} >>${LOGOUT} 2>>${LOGOUT}
126
127};
128
129# Run-command, halt if command exits with non-0
130rc_halt()
131{
132  CMD="$1"
133
134  if [ -z "${CMD}" ]
135  then
136    exit_err "Error: missing argument in rc_halt()"
137  fi
138
139  echo "Running: ${CMD}" >>${LOGOUT}
140  eval ${CMD} >>${LOGOUT} 2>>${LOGOUT}
141  STATUS="$?"
142  if [ "${STATUS}" != "0" ]
143  then
144    exit_err "Error ${STATUS}: ${CMD}"
145  fi
146};
147
148# Run-command w/echo to screen, halt if command exits with non-0
149rc_halt_echo()
150{
151  CMD="$1"
152
153  if [ -z "${CMD}" ]
154  then
155    exit_err "Error: missing argument in rc_halt_echo()"
156  fi
157
158  echo "Running: ${CMD}" >>${LOGOUT}
159  ${CMD} 2>&1 | tee -a ${LOGOUT} 
160  STATUS="$?"
161  if [ "$STATUS" != "0" ]
162  then
163    exit_err "Error ${STATUS}: $CMD"
164  fi
165
166};
167
168# Run-command w/echo, don't halt if command exits with non-0
169rc_nohalt_echo()
170{
171  CMD="$1"
172
173  if [ -z "${CMD}" ]
174  then
175    exit_err "Error: missing argument in rc_nohalt_echo()"
176  fi
177
178  echo "Running: ${CMD}" >>${LOGOUT}
179  ${CMD} 2>&1 | tee -a ${LOGOUT} 
180
181};
182
183# Echo to the screen and to the log
184echo_log()
185{
186  STR="$1"
187
188  if [ -z "${STR}" ]
189  then
190    exit_err "Error: missing argument in echo_log()"
191  fi
192
193  echo "${STR}" | tee -a ${LOGOUT} 
194};
195
196# Make sure we have a numeric
197is_num()
198{
199  expr $1 + 1 2>/dev/null
200  return $?
201}
202
203# Function which uses "fetch" to download a file, and display a progress report
204fetch_file()
205{
206
207  FETCHFILE="$1"
208  FETCHOUTFILE="$2"
209  EXITFAILED="$3"
210
211  EXITFILE="${TMPDIR}/.fetchExit"
212
213  rm ${FETCHOUTFILE} 2>/dev/null >/dev/null
214
215  SIZE=$(( `fetch -s "${FETCHFILE}"` / 1024 ))
216  echo "FETCH: ${FETCHFILE}"
217  echo "FETCH: ${FETCHOUTFILE}" >>${LOGOUT}
218
219  ( fetch -o ${FETCHOUTFILE} "${FETCHFILE}" >/dev/null 2>/dev/null ; echo "$?" > ${EXITFILE} ) &
220  PID="$!"
221  while
222  z=1
223  do
224
225    if [ -e "${FETCHOUTFILE}" ]
226    then
227      DSIZE=`du -k ${FETCHOUTFILE} | tr -d '\t' | cut -d '/' -f 1`
228      if [ $(is_num "$DSIZE") ] ; then
229      if [ $SIZE -lt $DSIZE ] ; then DSIZE="$SIZE"; fi 
230    	echo "SIZE: ${SIZE} DOWNLOADED: ${DSIZE}"
231    	echo "SIZE: ${SIZE} DOWNLOADED: ${DSIZE}" >>${LOGOUT}
232      fi
233    fi
234
235    # Check if the download is finished
236    ps -p ${PID} >/dev/null 2>/dev/null
237    if [ $? -ne 0 ]
238    then
239      break;
240    fi
241
242    sleep 2
243  done
244
245  echo "FETCHDONE"
246
247  EXIT="`cat ${EXITFILE}`"
248  if [ "${EXIT}" != "0" -a "$EXITFAILED" = "1" ]
249  then
250    exit_err "Error: Failed to download ${FETCHFILE}"
251  fi
252
253  return $EXIT
254
255};
256
257# Function to return a the zpool name for this device
258get_zpool_name()
259{
260  DEVICE="$1"
261
262  # Set the base name we use for zpools
263  BASENAME="tank"
264
265  if [ ! -d "${TMPDIR}/.zpools" ] ; then
266    mkdir -p ${TMPDIR}/.zpools
267  fi
268
269  if [ -e "${TMPDIR}/.zpools/${DEVICE}" ] ; then
270    cat ${TMPDIR}/.zpools/${DEVICE}
271    return 0
272  else
273    # Need to generate a zpool name for this device
274    NUM=`ls ${TMPDIR}/.zpools/ | wc -l | sed 's| ||g'`
275
276    # Is it used in another zpool?
277    while :
278    do
279      NEWNAME="${BASENAME}${NUM}"
280      zpool list | grep -qw "${NEWNAME}"
281      local chk1=$?
282      zpool import | grep -qw "${NEWNAME}"
283      local chk2=$?
284      if [ $chk1 -eq 1 -a $chk2 -eq 1 ] ; then break ; fi 
285      NUM=$((NUM+1))
286    done
287
288    # Now save the new tank name
289    mkdir -p ${TMPDIR}/.zpools/`dirname $DEVICE`
290    echo "$NEWNAME" >${TMPDIR}/.zpools/${DEVICE} 
291    echo "${NEWNAME}"
292    return 0
293  fi
294};
295
296iscompressed()
297{
298  local FILE
299  local RES
300
301  FILE="$1"
302  RES=1
303
304  if echo "${FILE}" | \
305    grep -qiE '\.(Z|lzo|lzw|lzma|gz|bz2|xz|zip)$' 2>&1
306  then
307    RES=0
308  fi
309
310  return ${RES}
311}
312
313get_compression_type()
314{
315  local FILE
316  local SUFFIX
317
318  FILE="$1"
319  SUFFIX=`echo "${FILE}" | sed -E 's|^(.+)\.(.+)$|\2|'`
320
321  VAL=""
322  SUFFIX=`echo "${SUFFIX}" | tr A-Z a-z`
323  case "${SUFFIX}" in
324    z) VAL="lzw" ;;
325    lzo) VAL="lzo" ;;
326    lzw) VAL="lzw" ;;
327    lzma) VAL="lzma" ;;
328    gz) VAL="gzip" ;;
329    bz2) VAL="bzip2" ;;
330    xz) VAL="xz" ;;
331    zip) VAL="zip" ;;
332  esac
333
334  export VAL
335}
336
337write_image()
338{
339  local DEVICE_FILE
340
341  IMAGE_FILE="$1"
342  DEVICE_FILE="$2"
343
344  if [ -z "${IMAGE_FILE}" ]
345  then
346    exit_err "ERROR: Image file not specified!"
347  fi
348 
349  if [ -z "${DEVICE_FILE}" ]
350  then
351    exit_err "ERROR: Device file not specified!"
352  fi
353 
354  if [ ! -f "${IMAGE_FILE}" ]
355  then
356    exit_err "ERROR: '${IMAGE_FILE}' does not exist!"
357  fi
358
359  DEVICE_FILE="${DEVICE_FILE#/dev/}"
360  DEVICE_FILE="/dev/${DEVICE_FILE}"
361 
362  if [ ! -c "${DEVICE_FILE}" ]
363  then
364    exit_err "ERROR: '${DEVICE_FILE}' is not a character device!"
365  fi
366
367  if iscompressed "${IMAGE_FILE}"
368  then
369	local COMPRESSION
370
371    get_compression_type "${IMAGE_FILE}"
372	COMPRESSION="${VAL}"
373
374    case "${COMPRESSION}" in
375      lzw)
376        rc_halt "uncompress ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}"
377        IMAGE_FILE="${IMAGE_FILE%.Z}"
378        ;;
379
380      lzo)
381        rc_halt "lzop -d $IMAGE_{FILE} -c | dd of=${DEVICE_FILE}"
382        IMAGE_FILE="${IMAGE_FILE%.lzo}"
383        ;;
384
385      lzma)
386        rc_halt "lzma -d ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}"
387        IMAGE_FILE="${IMAGE_FILE%.lzma}"
388        ;;
389
390      gzip)
391        rc_halt "gunzip ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}"
392        IMAGE_FILE="${IMAGE_FILE%.gz}"
393        ;;
394
395      bzip2)
396        rc_halt "bunzip2 ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}"
397        IMAGE_FILE="${IMAGE_FILE%.bz2}"
398        ;;
399
400      xz)
401        rc_halt "xz -d ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}"
402        IMAGE_FILE="${IMAGE_FILE%.xz}"
403        ;;
404
405      zip)
406        rc_halt "unzip ${IMAGE_FILE} -c | dd of=${DEVICE_FILE}"
407        IMAGE_FILE="${IMAGE_FILE%.zip}"
408        ;;
409
410      *) 
411        exit_err "ERROR: ${COMPRESSION} compression is not supported"
412        ;;
413    esac
414
415  else
416    rc_halt "dd if=${IMAGE_FILE} of=${DEVICE_FILE}"
417
418  fi
419};
420
421# Setup and install on a new disk / partition
422install_fresh()
423{
424  # Lets start setting up the disk slices now
425  setup_disk_slice
426  
427  if [ -z "${ROOTIMAGE}" ]
428  then
429
430    # Disk setup complete, now lets parse WORKINGSLICES and setup the bsdlabels
431    setup_disk_label
432  
433    # Now we've setup the bsdlabels, lets go ahead and run newfs / zfs 
434    # to setup the filesystems
435    setup_filesystems
436
437    # Lets mount the partitions now
438    mount_all_filesystems
439
440    # We are ready to begin extraction, lets start now
441    init_extraction 
442
443    # Check if we have any optional modules to load 
444    install_components
445
446    # Check if we have any packages to install
447    install_packages
448
449    # Do any localization in configuration
450    run_localize
451  
452    # Save any networking config on the installed system
453    save_networking_install
454
455    # Now add any users
456    setup_users
457
458    # Do any last cleanup / setup before unmounting
459    run_final_cleanup
460
461    # Now run any commands specified
462    run_commands
463
464    # Unmount and finish up
465    unmount_all_filesystems
466  fi
467
468  echo_log "Installation finished!"
469};
470
471# Extract the system to a pre-mounted directory
472install_extractonly()
473{
474  # We are ready to begin extraction, lets start now
475  init_extraction 
476
477  # Check if we have any optional modules to load 
478  install_components
479
480  # Check if we have any packages to install
481  install_packages
482
483  # Do any localization in configuration
484  run_localize
485
486  # Save any networking config on the installed system
487  save_networking_install
488
489  # Now add any users
490  setup_users
491
492  # Now run any commands specified
493  run_commands
494  
495  # Set a hostname on the install system
496  setup_hostname
497      
498  # Set the root_pw if it is specified
499  set_root_pw
500
501  echo_log "Installation finished!"
502};
503
504install_image()
505{
506  # We are ready to begin extraction, lets start now
507  init_extraction 
508
509  echo_log "Installation finished!"
510};
511
512install_upgrade()
513{
514  # We're going to do an upgrade, skip all the disk setup 
515  # and start by mounting the target drive/slices
516  mount_upgrade
517  
518  # Start the extraction process
519  init_extraction
520
521  # Do any localization in configuration
522  run_localize
523
524  # Now run any commands specified
525  run_commands
526  
527  # Merge any old configuration files
528  merge_old_configs
529
530  # Check if we have any optional modules to load 
531  install_components
532
533  # Check if we have any packages to install
534  install_packages
535
536  # All finished, unmount the file-systems
537  unmount_upgrade
538
539  echo_log "Upgrade finished!"
540};
541