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