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 which perform the extraction / installation of system to disk
29
30. ${BACKEND}/functions-mountoptical.sh
31
32# Performs the extraction of data to disk from a uzip or tar archive
33start_extract_uzip_tar()
34{
35  if [ -z "$INSFILE" ]
36  then
37    exit_err "ERROR: Called extraction with no install file set!"
38  fi
39
40  # Check if we have a .count file, and echo it out for a front-end to use in progress bars
41  if [ -e "${INSFILE}.count" ]
42  then
43    echo "INSTALLCOUNT: `cat ${INSFILE}.count`"
44  fi
45
46  # Check if we are doing an upgrade, and if so use our exclude list
47  if [ "${INSTALLMODE}" = "upgrade" ]
48  then
49   TAROPTS="-X ${PROGDIR}/conf/exclude-from-upgrade"
50  else
51   TAROPTS=""
52  fi
53
54  echo_log "pc-sysinstall: Starting Extraction"
55
56  case ${PACKAGETYPE} in
57    uzip)
58      if ! kldstat -v | grep -q "geom_uzip" ; then
59	exit_err "Kernel module geom_uzip not loaded"
60      fi
61
62	  # Start by mounting the uzip image
63      MDDEVICE=`mdconfig -a -t vnode -o readonly -f ${INSFILE}`
64      mkdir -p ${FSMNT}.uzip
65      mount -r /dev/${MDDEVICE}.uzip ${FSMNT}.uzip
66      if [ $? -ne 0 ]
67      then
68        exit_err "ERROR: Failed mounting the ${INSFILE}"
69      fi
70      cd ${FSMNT}.uzip
71
72      # Copy over all the files now!
73      tar cvf - . 2>/dev/null | tar -xpv -C ${FSMNT} ${TAROPTS} -f - 2>&1 | tee -a ${FSMNT}/.tar-extract.log
74      if [ $? -ne 0 ]
75      then
76        cd /
77        echo "TAR failure occurred:" >>${LOGOUT}
78        cat ${FSMNT}/.tar-extract.log | grep "tar:" >>${LOGOUT}
79        umount ${FSMNT}.uzip
80        mdconfig -d -u ${MDDEVICE}
81        exit_err "ERROR: Failed extracting the tar image"
82      fi
83
84      # All finished, now lets umount and cleanup
85      cd /
86      umount ${FSMNT}.uzip
87      mdconfig -d -u ${MDDEVICE}
88       ;;
89    tar)
90	  tar -xpv -C ${FSMNT} -f ${INSFILE} ${TAROPTS} >&1 2>&1
91      if [ $? -ne 0 ]
92      then
93        exit_err "ERROR: Failed extracting the tar image"
94      fi
95      ;;
96  esac
97
98  # Check if this was a FTP download and clean it up now
99  if [ "${INSTALLMEDIUM}" = "ftp" ]
100  then
101    echo_log "Cleaning up downloaded archive"
102    rm ${INSFILE} 
103    rm ${INSFILE}.count >/dev/null 2>/dev/null 
104    rm ${INSFILE}.md5 >/dev/null 2>/dev/null
105  fi
106
107  echo_log "pc-sysinstall: Extraction Finished"
108
109};
110
111# Performs the extraction of data to disk from a directory with split files
112start_extract_split()
113{
114  if [ -z "${INSDIR}" ]
115  then
116    exit_err "ERROR: Called extraction with no install directory set!"
117  fi
118
119  echo_log "pc-sysinstall: Starting Extraction"
120
121  # Used by install.sh
122  DESTDIR="${FSMNT}"
123  export DESTDIR
124
125  HERE=`pwd`
126  DIRS=`ls -d ${INSDIR}/*|grep -Ev '(uzip|kernels|src)'`
127  for dir in ${DIRS}
128  do
129    cd "${dir}"
130    if [ -f "install.sh" ]
131    then
132      echo_log "Extracting" `basename ${dir}`
133      echo "y" | sh install.sh >/dev/null
134      if [ $? -ne 0 ]
135      then
136        exit_err "ERROR: Failed extracting ${dir}"
137      fi
138    else
139      exit_err "ERROR: ${dir}/install.sh does not exist"
140    fi
141  done
142  cd "${HERE}"
143  
144  KERNELS=`ls -d ${INSDIR}/*|grep kernels`
145  cd "${KERNELS}"
146  if [ -f "install.sh" ]
147  then
148    echo_log "Extracting" `basename ${KERNELS}`
149    echo "y" | sh install.sh generic >/dev/null
150    if [ $? -ne 0 ]
151    then
152      exit_err "ERROR: Failed extracting ${KERNELS}"
153    fi
154    rm -rf "${FSMNT}/boot/kernel"
155    mv "${FSMNT}/boot/GENERIC" "${FSMNT}/boot/kernel"
156  else
157    exit_err "ERROR: ${KERNELS}/install.sh does not exist"
158  fi
159  cd "${HERE}"
160
161  SOURCE=`ls -d ${INSDIR}/*|grep src`
162  cd "${SOURCE}"
163  if [ -f "install.sh" ]
164  then
165    echo_log "Extracting" `basename ${SOURCE}`
166    echo "y" | sh install.sh all >/dev/null
167    if [ $? -ne 0 ]
168    then
169      exit_err "ERROR: Failed extracting ${SOURCE}"
170    fi
171  else
172    exit_err "ERROR: ${SOURCE}/install.sh does not exist"
173  fi
174  cd "${HERE}"
175
176  echo_log "pc-sysinstall: Extraction Finished"
177};
178
179# Function which will attempt to fetch the install file before we start
180# the install
181fetch_install_file()
182{
183  get_value_from_cfg ftpPath
184  if [ -z "$VAL" ]
185  then
186    exit_err "ERROR: Install medium was set to ftp, but no ftpPath was provided!" 
187  fi
188
189  FTPPATH="${VAL}"
190  
191  # Check if we have a /usr partition to save the download
192  if [ -d "${FSMNT}/usr" ]
193  then
194    OUTFILE="${FSMNT}/usr/.fetch-${INSFILE}"
195  else
196    OUTFILE="${FSMNT}/.fetch-${INSFILE}"
197  fi
198
199  # Do the fetch of the archive now
200  fetch_file "${FTPPATH}/${INSFILE}" "${OUTFILE}" "1"
201
202  # Check to see if there is a .count file for this install
203  fetch_file "${FTPPATH}/${INSFILE}.count" "${OUTFILE}.count" "0"
204
205  # Check to see if there is a .md5 file for this install
206  fetch_file "${FTPPATH}/${INSFILE}.md5" "${OUTFILE}.md5" "0"
207
208  # Done fetching, now reset the INSFILE to our downloaded archived
209  export INSFILE="${OUTFILE}"
210
211};
212
213# Function which will download freebsd install files
214fetch_split_files()
215{
216  get_ftpHost
217  if [ -z "$VAL" ]
218  then
219    exit_err "ERROR: Install medium was set to ftp, but no ftpHost was provided!" 
220  fi
221  FTPHOST="${VAL}"
222
223  get_ftpDir
224  if [ -z "$VAL" ]
225  then
226    exit_err "ERROR: Install medium was set to ftp, but no ftpDir was provided!" 
227  fi
228  FTPDIR="${VAL}"
229
230  # Check if we have a /usr partition to save the download
231  if [ -d "${FSMNT}/usr" ]
232  then
233    OUTFILE="${FSMNT}/usr/.fetch-${INSFILE}"
234  else
235    OUTFILE="${FSMNT}/.fetch-${INSFILE}"
236  fi
237
238  DIRS="base catpages dict doc games info manpages proflibs kernels src"
239  if [ "${FBSD_ARCH}" = "amd64" ]
240  then
241    DIRS="${DIRS} lib32"
242  fi
243
244  for d in ${DIRS}
245  do
246    mkdir -p "${OUTFILE}/${d}"
247  done
248
249
250  NETRC="${OUTFILE}/.netrc"
251  cat <<EOF >"${NETRC}"
252machine ${FTPHOST}
253login anonymous
254password anonymous
255macdef INSTALL
256bin
257prompt
258EOF
259
260  for d in ${DIRS}
261  do
262    cat <<EOF >>"${NETRC}"
263cd ${FTPDIR}/${d}
264lcd ${OUTFILE}/${d}
265mreget *
266EOF
267  done
268
269  cat <<EOF >>"${NETRC}"
270bye
271
272
273EOF
274
275  # Fetch the files via ftp
276  echo "$ INSTALL" | ftp -N "${NETRC}" "${FTPHOST}"
277
278  # Done fetching, now reset the INSFILE to our downloaded archived
279  export INSFILE="${OUTFILE}"
280}
281
282# Function which does the rsync download from the server specified in cfg
283start_rsync_copy()
284{
285  # Load our rsync config values
286  get_value_from_cfg rsyncPath
287  if [ -z "${VAL}" ]; then
288    exit_err "ERROR: rsyncPath is unset! Please check your config and try again."
289  fi
290  export RSYNCPATH="${VAL}"
291
292  get_value_from_cfg rsyncHost
293  if [  -z "${VAL}" ]; then
294    exit_err "ERROR: rsyncHost is unset! Please check your config and try again."
295  fi
296  export RSYNCHOST="${VAL}"
297
298  get_value_from_cfg rsyncUser
299  if [ -z "${VAL}" ]; then
300    exit_err "ERROR: rsyncUser is unset! Please check your config and try again."
301  fi
302  export RSYNCUSER="${VAL}"
303
304  get_value_from_cfg rsyncPort
305  if [ -z "${VAL}" ]; then
306    exit_err "ERROR: rsyncPort is unset! Please check your config and try again."
307  fi
308  export RSYNCPORT="${VAL}"
309
310  COUNT=1
311  while
312  z=1
313  do
314    if [ ${COUNT} -gt ${RSYNCTRIES} ]
315    then
316     exit_err "ERROR: Failed rsync command!"
317     break
318    fi
319
320    rsync -avvzHsR \
321    --rsync-path="rsync --fake-super" \
322    -e "ssh -p ${RSYNCPORT}" \
323    ${RSYNCUSER}@${RSYNCHOST}:${RSYNCPATH}/./ ${FSMNT}
324    if [ $? -ne 0 ]
325    then
326      echo "Rsync failed! Tries: ${COUNT}"
327    else
328      break
329    fi
330
331    COUNT=$((COUNT+1))
332  done 
333
334};
335
336start_image_install()
337{
338  if [ -z "${IMAGE_FILE}" ]
339  then
340    exit_err "ERROR: installMedium set to image but no image file specified!"
341  fi
342
343  # We are ready to start mounting, lets read the config and do it
344  while read line
345  do
346    echo $line | grep -q "^disk0=" 2>/dev/null
347    if [ $? -eq 0 ]
348    then
349      # Found a disk= entry, lets get the disk we are working on
350      get_value_from_string "${line}"
351      strip_white_space "$VAL"
352      DISK="$VAL"
353    fi
354
355    echo $line | grep -q "^commitDiskPart" 2>/dev/null
356    if [ $? -eq 0 ]
357    then
358      # Found our flag to commit this disk setup / lets do sanity check and do it
359      if [ -n "${DISK}" ]
360      then
361
362        # Write the image
363        write_image "${IMAGE_FILE}" "${DISK}"
364
365        # Increment our disk counter to look for next disk and unset
366        unset DISK
367        break
368
369      else
370        exit_err "ERROR: commitDiskPart was called without procceding disk<num>= and partition= entries!!!"
371      fi
372    fi
373
374  done <${CFGF}
375};
376
377# Entrance function, which starts the installation process
378init_extraction()
379{
380  # Figure out what file we are using to install from via the config
381  get_value_from_cfg installFile
382
383  if [ -n "${VAL}" ]
384  then
385    export INSFILE="${VAL}"
386  else
387    # If no installFile specified, try our defaults
388    if [ "$INSTALLTYPE" = "FreeBSD" ]
389    then
390      case $PACKAGETYPE in
391        uzip) INSFILE="${FBSD_UZIP_FILE}" ;;
392        tar) INSFILE="${FBSD_TAR_FILE}" ;;
393        split)
394          INSDIR="${FBSD_BRANCH_DIR}"
395
396          # This is to trick opt_mount into not failing
397          INSFILE="${INSDIR}"
398          ;;
399      esac
400    else
401      case $PACKAGETYPE in
402        uzip) INSFILE="${UZIP_FILE}" ;;
403        tar) INSFILE="${TAR_FILE}" ;;
404      esac
405    fi
406    export INSFILE
407  fi
408
409  # Lets start by figuring out what medium we are using
410  case ${INSTALLMEDIUM} in
411    dvd|usb)
412      # Lets start by mounting the disk 
413      opt_mount 
414      if [ -n "${INSDIR}" ]
415      then
416        INSDIR="${CDMNT}/${INSDIR}" ; export INSDIR
417	    start_extract_split
418
419      else
420        INSFILE="${CDMNT}/${INSFILE}" ; export INSFILE
421        start_extract_uzip_tar
422      fi
423      ;;
424
425    ftp)
426      if [ "$PACKAGETYPE" = "split" ]
427      then
428        fetch_split_files
429
430        INSDIR="${INSFILE}" ; export INSDIR
431        start_extract_split
432      else
433        fetch_install_file
434        start_extract_uzip_tar 
435      fi
436      ;;
437
438    sftp) ;;
439
440    rsync) start_rsync_copy ;;
441    image) start_image_install ;;
442    local)
443      get_value_from_cfg localPath
444      if [ -z "$VAL" ]
445      then
446        exit_err "Install medium was set to local, but no localPath was provided!"
447      fi
448      LOCALPATH=$VAL
449      INSFILE="${LOCALPATH}/${INSFILE}" ; export INSFILE
450      start_extract_uzip_tar
451      ;;
452    *) exit_err "ERROR: Unknown install medium" ;;
453  esac
454
455};
456