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