4. Stage One: System Installation

The first version of this article used a single shell script for stage one where all your customization had to be done by editing the script. After valuable user feedback I have decided to separate the code and data in the scripts. This allows to have different configuration data sets to install different systems without changing any of the code scripts.

The code script for stage one is stage_1.sh and when run with exactly one argument, like

# ./stage_1.sh default

will read its configuration from stage_1.conf.default and write a log to stage_1.log.default.

Further below you find my stage_1.conf.default. You need to customize it in various places to match your idea of the perfect system. I have tried to extensively comment the places you should adapt. The configuration script must provide four shell functions, create_file_systems, create_etc_fstab, copy_files and all_remaining_customization (in case it matters: this is also the sequence in which they will be called from stage_1.sh).

The points to ponder are:

Before you run stage_1.sh make sure you have completed the usual tasks in preparation for make installworld installkernel, like:

When you run stage_1.sh for the first time, and the config files copied from your running system to the new system are not up-to-date with respect to what is under /usr/src, mergemaster will ask you how to proceed. I recommend merging the changes. If you get tired of going through the dialogues you can simply update the files on your running system once (Only if this is an option. You probably do not want to do this if one of your systems runs -STABLE and the other -CURRENT. The changes may be incompatible). Subsequent mergemaster invocations will detect that the RCS version IDs match those under /usr/src and skip the file.

The stage_1.sh script will stop at the first command that fails (returns a non-zero exit status) due to set -e, so you cannot overlook errors. It will also stop if you use an unset environment variable, probably due to a typo. You should correct any errors in your version of stage_1.conf.default before you go on.

In stage_1.sh we invoke mergemaster. Even if none of the files requires a merge, it will display and ask at the end

*** Comparison complete
*** Saving mtree database for future upgrades

Do you wish to delete what is left of /var/tmp/temproot.stage1? [no] no

Please answer no or just hit Enter. The reason is that mergemaster will have left a few zero sized files below /var/tmp/temproot.stage1 which will be copied to the new system later (unless already there).

After that mergemaster will list the files it installed and ask if the new login.conf should be generated:

*** You chose the automatic install option for files that did not
    exist on your system.  The following were installed for you:

*** You installed a new aliases file into /newroot/etc/mail, but
    the newaliases command is limited to the directories configured
    in sendmail.cf.  Make sure to create your aliases database by
    hand when your sendmail configuration is done.

*** You installed a login.conf file, so make sure that you run
    '/usr/bin/cap_mkdb /newroot/etc/login.conf'
     to rebuild your login.conf database

    Would you like to run it now? y or n [n]

The answer does not matter since stage_1.sh will run cap_mkdb(1) for you in any case.

Here is the author's stage_1.conf.default, which you need to modify substantially. The comments give you enough information what to change.

# This file: stage_1.conf.default, sourced by stage_1.sh.
# $Id: stage_1.conf.default,v 1.5 2011-05-14 20:44:31 hrs Exp $
# $FreeBSD: head/en_US.ISO8859-1/articles/fbsd-from-scratch/stage_1.conf.default 38826 2012-05-17 19:12:14Z hrs $

# Root mount point where you create the new system. Because it is only
# used as a mount point, no space will be used on that file system as all
# files are of course written to the mounted file system(s).

# Where your src tree is.

# Where your obj is.

# Your kernel config name as from make buildkernel KERNCONF=...

# Your target architecture as used for make buildworld TARGET=...
# If you did not specify a TARGET when building world, it defaulted
# to the build architecture (run "uname -m" to find out if you are unsure).
TARGET="i386"  # amd64 arm i386 ia64 mips pc98 powerpc sparc64

# Available time zones are those under /usr/share/zoneinfo.

# The create_file_systems function must create the mountpoints under
# DESTDIR, create the file systems, and then mount them under DESTDIR.
create_file_systems () {
  # The new root file system. Mandatory.
  # Change DEVICE names.
  mkdir -m 755 -p ${DESTDIR}
  chown root:wheel ${DESTDIR}
  newfs -U ${DEVICE}
  mount -o noatime ${DEVICE} ${DESTDIR}

  # Additional file systems and initial mount points. Optional.
  mkdir -m 755 -p ${DESTDIR}/var
  chown root:wheel ${DESTDIR}/var
  newfs -U ${DEVICE}
  mount -o noatime ${DEVICE} ${DESTDIR}/var

  mkdir -m 755 -p ${DESTDIR}/usr
  chown root:wheel ${DESTDIR}/usr
  newfs -U ${DEVICE}
  mount -o noatime ${DEVICE} ${DESTDIR}/usr

# The create_etc_fstab function must create an fstab matching the
# file systems created in create_file_systems.
create_etc_fstab () {
  cat <<EOF >${DESTDIR}/etc/fstab
# Device         Mountpoint          FStype    Options              Dump Pass#
/dev/da0s1b      none                swap      sw                   0    0
/dev/da1s1b      none                swap      sw                   0    0
/dev/da2s2b      none                swap      sw                   0    0
/dev/da3s2b      none                swap      sw                   0    0
/dev/da0s1a      /                   ufs       rw,noatime           1    1
/dev/da0s1e      /var                ufs       rw,noatime           1    1
/dev/da2s1e      /usr                ufs       rw,noatime           1    1
/dev/vinum/Share /share              ufs       rw,noatime           0    2
/dev/vinum/home  /home               ufs       rw,noatime           0    2
/dev/vinum/ncvs  /home/ncvs          ufs       rw,noatime           0    2
/dev/vinum/ports /usr/ports          ufs       rw,noatime           0    2
/dev/ad1s1a      /flash              ufs       rw,noatime           0    0
/dev/ad0s1       /2k                 ntfs      ro,noauto            0    0
/dev/ad0s6       /linux              ext2fs    ro,noauto            0    0
/dev/cd0         /cdrom              cd9660    ro,noauto            0    0
/dev/cd1         /dvd                cd9660    ro,noauto            0    0
proc             /proc               procfs    rw                   0    0
linproc          /compat/linux/proc  linprocfs rw                   0    0
  chmod 644 ${DESTDIR}/etc/fstab
  chown root:wheel ${DESTDIR}/etc/fstab

# The copy_files function is used to copy files before mergemaster is run.
copy_files () {
  # Add or remove from this list at your discretion. Mostly mandatory.
  for f in \
    /.profile \
    /etc/devd.conf \
    /etc/devd.rules \
    /etc/exports \
    /etc/group \
    /etc/hosts \
    /etc/inetd.conf \
    /etc/ipfw.conf \
    /etc/make.conf \
    /etc/master.passwd \
    /etc/nsswitch.conf \
    /etc/ntp.conf \
    /etc/printcap \
    /etc/profile \
    /etc/rc.conf \
    /etc/resolv.conf \
    /etc/src.conf \
    /etc/sysctl.conf \
    /etc/ttys \
    /etc/mail/aliases \
    /etc/mail/aliases.db \
    /etc/mail/hal9000.mc \
    /etc/mail/service.switch \
    /etc/ssh/*key* \
    /etc/ssh/*_config \
    /etc/X11/xorg.conf \
    /var/cron/tabs/* \
    /root/.profile \
    /boot/*.bmp \
    /boot/loader.conf \
    /boot/device.hints ; do
    cp -p ${f} ${DESTDIR}${f}

# Everything else you want to tune in the new system.
# NOTE: Do not install too many binaries here. With the old system running and
# the new binaries and headers installed you are likely to run into bootstrap
# problems. Ports should be compiled after you have booted in the new system.
all_remaining_customization () {
  # Without the compat symlink the linux_base files end up on the root fs:
  cd ${DESTDIR}
  mkdir -m 755 usr/compat; chown root:wheel usr/compat; ln -s usr/compat
  mkdir -m 755 usr/compat/linux;      chown root:wheel usr/compat/linux
  mkdir -m 555 usr/compat/linux/proc; chown root:wheel usr/compat/linux/proc
  mkdir -m 755 boot/grub;             chown root:wheel boot/grub
  mkdir -m 755 linux 2k;              chown root:wheel linux 2k
  mkdir -m 755 src;                   chown root:wheel src
  mkdir -m 755 share;                 chown root:wheel share
  mkdir -m 755 dvd cdrom flash;       chown root:wheel dvd cdrom flash
  mkdir -m 755 home;                  chown root:wheel home
  mkdir -m 755 usr/ports;             chown root:wheel usr/ports

  # Create the ntp and slip log files.
  touch ${DESTDIR}/var/log/ntp ${DESTDIR}/var/log/slip.log

  # Make /usr/src point to the right directory. Optional.
  # Note: some ports need part of the src tree, e.g. emulators/kqemu,
  # sysutils/lsof, sysutils/fusefs, ...
  cd ${DESTDIR}/usr
  if test "${SRC}" != /usr/src; then
    rmdir src; ln -s ${SRC}
  if test "${MAKEOBJDIRPREFIX}" != /usr/obj; then
    rmdir obj; ln -s ${MAKEOBJDIRPREFIX}

  # My personal preference is to symlink tmp -> var/tmp. Optional.
  cd ${DESTDIR}; rmdir tmp; ln -s var/tmp

  # Make spooldirs for the printers in my /etc/printcap.
  cd ${DESTDIR}/var/spool/output/lpd; mkdir -p as od ev te lp da
  touch ${DESTDIR}/var/log/lpd-errs

  # If you do not have /home on a shared partition, you may want to copy it:
  # mkdir -p ${DESTDIR}/home
  # cd /home; tar cf - . | (cd ${DESTDIR}/home; tar xpvf -)

# vim: tabstop=2:expandtab:shiftwidth=2:syntax=sh:
# EOF $RCSfile: stage_1.conf.default,v $

Download stage_1.conf.default .

Running this script installs a system that when booted provides:

Other areas are prepared for configuration, but will not work until stage two is completed. For example we have copied files to configure printing and X11. Printing however is likely to need applications not found in the base system, like PostScript® utilities. X11 will not run before we have compiled the server, libraries and programs.

All FreeBSD documents are available for download at http://ftp.FreeBSD.org/pub/FreeBSD/doc/

Questions that are not answered by the documentation may be sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.