FreeBSD をゼロから設定するには

Jens Schweikhardt

$FreeBSD: doc/ja_JP.eucJP/articles/fbsd-from-scratch/article.sgml,v 1.4 2004/08/08 13:43:59 hrs Exp $

FreeBSD は Wind River Systems, Inc. の登録商標です。 これは近いうちに変わる見込みです。

Adobe, Acrobat, Acrobat Reader および PostScript は アメリカ合衆国および/またはその他の国の Adobe Systems Incorporated の登録商標または商標です。

製造者および販売者が製品を区別するのに 用いている表示の多くは、商標とされています。 この文書に登場する表示のうち FreeBSD Project がその商標を確認しているものには、その表示に続いて ``™'' または ``®'' 記号がおかれています。


この記事は、「FreeBSD をゼロから設定する (FreeBSD From Scratch)」という、 わたしの個人的な経験をまとめたものです。 カスタマイズした FreeBSD システムをソースからコンパイルし、 さらに好みの ports のコンパイルして、 あなたが望む構成のシステムの、 完全に自動化されたインストールを実現します。 make world がすばらしい考え方だとお思いの方にとって、 「FreeBSD をゼロから設定する」は、まさに make worldmake evenmore (さらにその先) へと広げるものになることでしょう。


1. はじめに

今までに make world を使ってシステムをアップグレードした経験はあるでしょうか? もしディスクに一つのシステムしか入れていない場合は問題です。 installworld が途中で止まってしまったら、 あなたのシステムは壊れたまま、もう起動しなくなってしまうかも知れません。 あるいは、installworld が正常に終了しても、 新しいカーネルは起動に失敗してしまうかも知れません。 さて、そうなってしまったら、Fixit CD を取り出して半年前のバックアップを戻す、 なんてはめになってしまうかも知れませんよね。

わたしは、``アップグレードの時はディスクを初期化する'' という方法がよいと考えています。パーティションではなくディスク全体のデータを 消去することで、アップグレードの手順では無視されるような古いデータが 残ってしまうことを防ぐことができます。ただ、 パーティションを全部初期化するということは、 ports/packages をすべて再コンパイル・再インストールしなければならず、 設定ファイルも注意深く作成し直さなければならないということです。 こういう作業を自動化したいと思いませんか? そう思う人は、この先を読み進めましょう。


2. どうして「FreeBSD をゼロから設定する」(あるいは「〜しない」) ことが必要なのか

これはもっともな質問です。 すでに sysinstall がありますし、 カーネルとユーザランドツールをコンパイルする方法には、 もっと有名な方法が他にもあるからです。

sysinstall の問題は、「何を、どこに、 どうやってインストールするのか」が非常に限定されているという点です。

システム全体を構築してインストールする方法は、 ハンドブックにある方法が有名です。 これはデフォルトで既存のシステムを置き換えるもので、 カーネルとモジュールだけが保存され、 システムバイナリ、ヘッダ、その他の多くのファイルは上書きされます。 使われなくなった古いファイルはそのまま残り、 動作に問題が出ることもあります。 何らかの理由でアップグレードに失敗すると、 システムを元の状態に戻することは不可能か、できても非常に困難です。

FreeBSD をゼロから設定する」方法は、これらの問題をすべて解決できます。 考え方は単純です。 稼働中のシステムを使って空のディレクトリにシステムをインストールします。 その時、その新しいシステムのディレクトリツリーには、 新しいパーティションを適切にマウントしておaきます。 数多くある設定ファイルは、コピーできるものは適切な場所にコピーし、 それができないものには mergemaster(8) を使います。 新しいシステムに対するインストール後の設定は、 古いシステムを動作させながら、新しいシステムに対して chroot して 自由に行なうことができます。具体的には、 シェルスクリプト、もしくは make の実行で構成される、次の 3 段階でこれらを実現します。

  1. stage_1.sh: 新しい起動可能なシステムを空のディレクトリ以下に作成し、 必要なファイルをマージ、もしくはコピーします。 そして、新しいシステムを起動します。

  2. stage_2.sh: 必要な ports をインストールします。

  3. stage_3.mk: ひとつ前の段階でインストールしたソフトウェアの、 インストール後の設定を行ないます。

新しいシステムを構築するために「FreeBSD をゼロから設定する」方法を使い、 それが数週間、満足する程度に動作していることを確認したら、 もう一度それを使って、大元のシステムを再インストールすることができます。 これからはいつでも好きな時にシステムを更新して、 初期化・再インストールしたパーティションに切り替えるだけでよくなるわけです。

Linux From Scratch (もしくは省略して LFS) について耳にしたり、試された方がいらっしゃるかも知れません。 LFS も同じように、稼働中のシステムを使ってシステムをゼロから構築し、 空のパーティションにインストールする方法が書かれています。 LFS が話題の中心としているのは、(カーネル、コンパイラ、デバイス、 シェル、端末データベースなどの) 各システムコンポーネントの役割と、 それらのインストールの詳細を見せることのようです。 この「FreeBSD をゼロから設定する」では、そのような詳細には触れません。 わたしの目的は、インストールを終わりまで自動化することであり、 システム構築時の泥くさい過程を全部説明することではありません。 FreeBSD をそのようなレベルで掘り下げてみたい人は、 /usr/src/Makefile を読んで、 make buildworld の動作を追いかけるところから始めましょう。

また、「FreeBSD をゼロから設定する」方法にも、 次のような欠点があることを心に留めておいてください。


3. 前提とする環境

FreeBSD をゼロから設定する」方法を実行するには、 次のものが必要です。


4. 第 1 段階: システムのインストール

次に紹介するのは、わたしが作成した stage_1.sh です。あなたが``求めているシステム''に合うように、 カスタマイズしてください。カスタマイズしなければならないところには、 なるべく詳細なコメントを付けてあります。重要なポイントは、 以下のとおりです。

stage_1.sh を実行する前に、 make installworld installkernel を実行するために通常行なう作業を完了させておいてください。 これらは、たとえば次のようなものです。

初めて stage_1.sh を実行した場合は、 稼働中のシステムから新しいシステムへとコピーされる設定ファイルは /usr/src のものと比べると古いので、 mergemaster がどうするかを聞いてきます。 おすすめは、ここで変更点を統合しておくことです。 もし、何度も質問に答えるのが面倒であれば、 稼働中のシステムのファイルを更新しておきましょう (ただしこれは、そうできればの話です。 -STABLE のシステムを実行していて、 -CURRENT を構築する、 もしくはその逆のようなケースでは、そうしてはいけません)。 次に mergemaster を実行した時、 RCS バージョン ID が /usr/src にあるファイルと一致しているものは、処理が飛ばされるようになります。

stage_1.sh スクリプトは set -e が指定されており、 最初のコマンドが失敗 (終了コードが 0 以外) すると停止します。 そのため、エラーを見逃してしまうということはないでしょう。 次に進む前に、stage_1.sh にあるエラーを全部修正しておいてください。

stage_1.sh では mergemaster が実行されます。 統合作業をしなければならないファイルが一つもない状態でも、 実行の終わりに次のメッセージが表示されます。

*** Comparison complete

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

no と答えるか、 単に Enter を押してください。 なぜかと言うと、mergemaster/var/tmp/temproot.stage1 にサイズが 0 のファイルをいくつか残すからです。 これは、後で新しいシステムに (存在しなければ) コピーされます。

この後、インストールされたファイルのリストがページャ (デフォルトでは more(1) です。less(1) を使うこともできます) に表示されます。

*** You chose the automatic install option for files that did not
    exist on your system.  The following were installed for you:
      /newroot/etc/defaults/rc.conf
      ...
      /newroot/COPYRIGHT

(END)

q を入力してページャを終了します。 すると login.conf に関して、次のように表示されます。

*** 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]

これに対する答えはどちらでも構いません。 どう答えても、スクリプトから cap_mkdb(1) が実行されます。

ちゃんと予想どおりに動いているかチェックできるよう、 stage_1.sh で行なわれたことは、 すべて stage_1.log に記録されます。

次に示すのは、筆者の使っている stage_1.sh です。 特にステップ 1, 2, 5, 6 は書き換える必要があるでしょう。

Warningnewfs(8) コマンドには注意してください。 マウントずみのパーティションに新しいファイルシステムを作成することはできないものの、 このスクリプトはマウントされていない /dev/da3s1a, /dev/vinum/var_a, /dev/vinum/usr_a をすべて削除します。 ひとつ間違えれば、あなたの環境を破壊してしまう可能性がありますので、 デバイス名の変更は注意深く行なってください。

#!/bin/sh
#
# stage_1.sh - FreeBSD From Scratch, 第 1 段階: システムのインストール
#              使い方: ./stage_1.sh
#
# $FreeBSD: doc/ja_JP.eucJP/articles/fbsd-from-scratch/stage_1.sh,v 1.1 2003/10/11 15:06:56 hrs Exp $
# Original revision: 1.1

set -x -e
PATH=/bin:/usr/bin:/sbin:/usr/sbin

# 前提とする環境:
#
# a) "make buildworld" と "make buildkernel" が正常に終了していること。
# b) 未使用パーティションがあること (ルートファイルシステム用に少なくとも 1 個、
#    好みに応じて /usr や /var 用のものを用意する)

# 新しいシステムを作成する場所を示すルートマウントポイントを指定。
# マウントポイントとして使われるだけなので、マウントポイントのある
# ファイルシステムにファイルは置かれず、書き込みはすべてマウントした
# ファイルシステムに行なわれる。
DESTDIR=/newroot
SRC=/usr/src         # src ツリーのある場所

# ---------------------------------------------------------------------------- #
# ステップ 1: $DESTDIR 以下に空のディレクトリツリーを作成
# ---------------------------------------------------------------------------- #

step_one () {
  # 新しいルートファイルシステムを作成する。必須。
  # デバイス名 (DEV_*) を変更すること。変更しないとシステムが壊れる危険性がある。
  DEV_ROOT=/dev/da3s1a
  mkdir -p ${DESTDIR}
  newfs ${DEV_ROOT}
  tunefs -n enable ${DEV_ROOT}
  mount -o noatime ${DEV_ROOT} ${DESTDIR}

  # その他のファイルシステムと初期マウントポイント。オプション。
  DEV_VAR=/dev/vinum/var_a
  newfs ${DEV_VAR}
  tunefs -n enable ${DEV_VAR}
  mkdir -m 755 ${DESTDIR}/var
  mount -o noatime ${DEV_VAR} ${DESTDIR}/var

  DEV_USR=/dev/vinum/usr_a
  newfs ${DEV_USR}
  tunefs -n enable ${DEV_USR}
  mkdir -m 755 ${DESTDIR}/usr
  mount -o noatime ${DEV_USR} ${DESTDIR}/usr

  mkdir -m 755 -p ${DESTDIR}/usr/ports
  mount /dev/vinum/ports ${DESTDIR}/usr/ports

  # ここで他のすべてのディレクトリを作成。必須。
  cd ${SRC}/etc; make distrib-dirs DESTDIR=${DESTDIR}
  # 個人的には tmp -> var/tmp とシンボリックリンクを張るのが好み。オプション。
  cd ${DESTDIR}; rmdir tmp; ln -s var/tmp
}

# ---------------------------------------------------------------------------- #
# ステップ 2: /etc ディレクトリツリーと / にファイルを追加
# ---------------------------------------------------------------------------- #

step_two () {
  # 好みに応じて、このリストに追加・削除すること。ほとんどの場合は必須。
  for f in \
    /.profile \
    /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/start_if.xl0 \
    /etc/ttys \
    /etc/ppp/* \
    /etc/mail/aliases \
    /etc/mail/aliases.db \
    /etc/mail/hal9000.mc \
    /etc/mail/service.switch \
    /etc/ssh/*key* \
    /etc/ssh/*_config \
    /etc/X11/XF86Config-4 \
    /boot/splash.bmp \
    /boot/loader.conf \
    /boot/device.hints ; do
    cp -p ${f} ${DESTDIR}${f}
  done
  # mergemaster の作業ファイルがあれば削除。
  TEMPROOT=/var/tmp/temproot.stage1
  if test -d ${TEMPROOT}; then
    chflags -R 0 ${TEMPROOT}
    rm -rf ${TEMPROOT}
  fi
  mergemaster -i -m ${SRC}/etc -t ${TEMPROOT} -D ${DESTDIR}
  cap_mkdb ${DESTDIR}/etc/login.conf
  pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd

  # mergemaster は /var/log に置かれる空ファイルを作成しないので、
  # ここで作成。ただし上のループでコピーされている場合は、それを使う。
  cd ${TEMPROOT}
  find . -type f | sed 's,^\./,,' |
  while read f; do
    if test -r ${DESTDIR}/${f}; then
      echo "${DESTDIR}/${f} already exists; not copied"
    else
      echo "Creating empty ${DESTDIR}/${f}"
      cp -p ${f} ${DESTDIR}/${f}
    fi
  done
  chflags -R 0 ${TEMPROOT}
  rm -rf ${TEMPROOT}
}

# ---------------------------------------------------------------------------- #
# ステップ 3: installworld を実行する
# ---------------------------------------------------------------------------- #

step_three () {
  cd ${SRC}
  make installworld DESTDIR=${DESTDIR}
}

# ---------------------------------------------------------------------------- #
# ステップ 4: カーネルとモジュールをインストールする
# ---------------------------------------------------------------------------- #

step_four () {
  cd ${SRC}
  # installkernel ターゲットには、loader.conf と device.hints が必要。 
  # ステップ 2 でコピーしていなければ、次の 2 行を使ってコピーすること。
  #   cp sys/boot/forth/loader.conf ${DESTDIR}/boot/defaults
  #   cp sys/i386/conf/GENERIC.hints ${DESTDIR}/boot/device.hints
  make installkernel DESTDIR=${DESTDIR} KERNCONF=HAL9000
}

# ---------------------------------------------------------------------------- #
# ステップ 5: 必須のファイルのインストールと変更
# ---------------------------------------------------------------------------- #

step_five () {
  # /etc/fstab の作成。必須。自分のデバイスに合うように変更すること。
  cat <<EOF >${DESTDIR}/etc/fstab
# Device         Mountpoint          FStype    Options              Dump Pass#
/dev/da3s1b      none                swap      sw                   0    0
/dev/da4s2b      none                swap      sw                   0    0
/dev/da3s1a      /                   ufs       rw                   1    1
/dev/da1s2a      /src                ufs       rw                   0    2
/dev/da2s2f      /share              ufs       rw                   0    2
/dev/vinum/var_a /var                ufs       rw                   0    2
/dev/vinum/usr_a /usr                ufs       rw                   0    2
/dev/vinum/home  /home               ufs       rw                   0    2
/dev/vinum/ncvs  /home/ncvs          ufs       rw,noatime           0    2
/dev/vinum/ports /usr/ports          ufs       rw,noatime           0    2
#
/dev/cd0         /dvd                cd9660    ro,noauto            0    0
/dev/cd1         /cdrom              cd9660    ro,noauto            0    0
proc             /proc               procfs    rw                   0    0
EOF

  # その他のディレクトリ。オプション。
  mkdir -m 755 -p ${DESTDIR}/src;       chown root:wheel ${DESTDIR}/src
  mkdir -m 755 -p ${DESTDIR}/share;     chown root:wheel ${DESTDIR}/share
  mkdir -m 755 -p ${DESTDIR}/dvd;       chown root:wheel ${DESTDIR}/dvd
  mkdir -m 755 -p ${DESTDIR}/home;      chown root:wheel ${DESTDIR}/home
  mkdir -m 755 -p ${DESTDIR}/usr/ports; chown root:wheel ${DESTDIR}/usr/ports
  # タイムゾーンの設定。ほとんどの場合は必須。
  cp ${DESTDIR}/usr/share/zoneinfo/Europe/Berlin ${DESTDIR}/etc/localtime
  if test -r /etc/wall_cmos_clock; then
     cp -p /etc/wall_cmos_clock ${DESTDIR}/etc/wall_cmos_clock
  fi
}

# ---------------------------------------------------------------------------- #
# ステップ 6: 新しいシステムにログインする時に重要な内容
# 注意: あまり多くのバイナリをこの時点でインストールしないこと。稼働している
# 古いシステムと、インストールした新しいバイナリ・ヘッダを組み合わせると、
# ブートストラップ問題に陥る可能性がある。ports は新しいシステムが起動した後に
# 再構築する方がよい。
# ---------------------------------------------------------------------------- #

step_six () {
  chroot ${DESTDIR} sh -c "cd /usr/ports/shells/zsh; make clean install clean"
  chroot ${DESTDIR} sh -c "cd /etc/mail; make install"  # configure sendmail

  # compat シンボリックリンクがないと、linux_base のファイル群が
  # ルートファイルシステムに置かれてしまう。
  cd ${DESTDIR}; mkdir -m 755 usr/compat
  chown root:wheel usr/compat; ln -s usr/compat
  mkdir -m 755 usr/compat/linux
  mkdir -m 755 boot/grub

  # /etc/printcap で指定したスプールディレクトリを作成。
  cd ${DESTDIR}/var/spool/output/lpd; mkdir -p as od ev te lp da
  touch ${DESTDIR}/var/log/lpd-errs

  # 古いシステムから引き継ぎたいファイルを指定
  for f in \
    /var/cron/tabs/root \
    /var/mail/* \
    /boot/grub/*; do
    cp -p ${f} ${DESTDIR}${f}
  done

  # 共有パーティション /home がなければ、/home をコピーした方がよいかも知れない。
  # mkdir -p ${DESTDIR}/home
  # cd /home; tar cf - . | (cd ${DESTDIR}/home; tar xpvf -)

  # FreeBSD 5.x より perl は /usr/local/bin に置かれるようになったが、
  # 多くのスクリプトは #!/usr/bin/perl でハードコードされている。
  # これらを動作させるため、シンボリックリンクを作成しておく。
  cd ${DESTDIR}/usr/bin; ln -s ../local/bin/perl
  cd ${DESTDIR}/usr; rmdir src; ln -s ../src/current src
}

do_steps () {
  step_one
  step_two
  step_three
  step_four
  step_five
  step_six
}

do_steps 2>&1 | tee stage_1.log

# EOF $RCSfile: stage_1.sh,v $    vim: tabstop=2:expandtab:

このスクリプトを実行すると、 起動した時に次のような状態になっているシステムがインストールされます。

他の部分に対する設定は、第 2 段階が終わるまで動作しません。 たとえば、プリンタや X11 の設定ファイルもコピーされますが、 プリンタは PostScript® ユーティリティなど、 ベースシステムに含まれないアプリケーションを使うことが多いでしょう。 X11 はサーバ、ライブラリ、プログラムをコンパイルしないと動作しません。


5. 第 2 段階: ports のインストール

Note: この段階で ports をコンパイルするのではなく、 (コンパイルずみの) packages をインストールすることもできます。 その場合、stage_2.sh は 単に pkg_add コマンドを羅列するだけになるでしょう。 読者のみなさんにとって、そういうスクリプトを書くのは難しくないと思いますので、 ここではもっと柔軟で、ports を使った伝統的な方法について考えることにします。

次に紹介する stage_2.sh スクリプトは、 わたしが好みの ports をインストールするために使ったものです。 これは何度でも実行でき、インストールずみの ports があれば、 飛ばして処理されます。スクリプトは 実行せず、実行される内容だけ を表示する (dryrun) オプション (-n) があります。ports リストの編集や、環境変数の設定を変更しましょう。

ports リストは、空白で区切られた 2 個以上のキーワードからなっています。 カテゴリ、port 名に始まり、オプションとして port をコンパイルしてインストールするためのコマンド (デフォルトは make install) が続きます。 空白行と # から始まる行は無視されます。 おそらく多くの場合に考えなければならないのは、カテゴリ名と port 名だけでしょう。 ports によっては、たとえば次のように make 変数を使って微調整することができます。

www mozilla make WITHOUT_MAILNEWS=yes WITHOUT_CHATZILLA=yes install
mail procmail make BATCH=yes install

実際には任意のシェルコマンドを指定できますので、 make を使う以外にも応用は可能です。

java linux-sun-jdk13 yes | make install
news inn-stable CONFIGURE_ARGS="--enable-uucp-rnews --enable-setgid-inews" make install

news/inn-stable の行は、 CONFIGURE_ARGS という シェル変数を定義した例です。 この port の Makefile は、 この指定した値を変数の初期値として、その他の必須の引数と一緒に使います。 これと

news inn-stable make CONFIGURE_ARGS="--enable-uucp-rnews --enable-setgid-inews" install

のようにして make 変数をコマンドラインに設定した場合との違いは、 こちらの場合に変数そのものを完全に上書きしてしまうという点です。 どの方法を使えばいいのかについては、各 port によります。

インストールしたい ports が、 対話的インストールを使っていないことを確認してください。 ports は、あなたが標準入力に明示的に指定したもの以外、 標準入力を読み込む動作をしてはいけません。 もし ports がそのように作られていると、ports はヒアドキュメントにある ports リストの次の行を読み込んで混乱してしまいます。 stage_2.sh を実行した時、 ある port が飛ばされたり、動作が止まってしまうようなことがあれば、 おそらくこれが原因でしょう。

次に示すのが、実際の stage_2.sh です。 これは、インストールされる port それぞれに対して LOGDIR/category+port という名前のログファイルを作成します。 stage_2.sh が共有パーティションになければ、 実行前に新しいシステムにこれをコピーするようにしてください。

#!/bin/sh
#
# stage_2.sh - FreeBSD From Scratch, 第 2 段階: ports のインストール
#              使い方: ./stage_2.sh
#
# $FreeBSD: doc/ja_JP.eucJP/articles/fbsd-from-scratch/stage_2.sh,v 1.1 2003/10/11 15:06:56 hrs Exp $
# Original revision: 1.1

DBDIR=/var/db/pkg
PORTS=/usr/ports
LOGDIR=/home/root/setup/ports.log; mkdir -p ${LOGDIR}

# 複数の port で使用される環境変数の設定
PAPERSIZE=a4;    export PAPERSIZE
USA_RESIDENT=NO; export USA_RESIDENT

MYNAME=$(basename $0)
usage () {
    exec >&2
    echo "usage: ${MYNAME} [-hn]"
    echo ""
    echo "  Options:"
    echo "  -h    Print this help text."
    echo "  -n    Dryrun: just show what would be done."
    echo ""
    exit 1
}

args=`getopt hn $*`
if test $? != 0; then
    usage
fi
set -- $args
DRYRUN=
for i; do
    case "$i" in
    -n) DRYRUN=yes;;
    --) break;;
    *) usage;;
    esac
done

cat << EOF |
lang perl5
security sudo
x11-servers XFree86-4-Server
x11 wrapper
x11 XFree86-4-libraries
x11 XFree86-4-clients
x11-fonts XFree86-4-font75dpi
x11-fonts XFree86-4-font100dpi
x11-fonts XFree86-4-fontScalable
x11-fonts urwfonts
x11-fonts webfonts
x11-toolkits open-motif
x11 rxvt
x11-wm ctwm
security openssh-askpass
astro xplanet
astro setiathome make BATCH=yes install
astro xephem
editors vim
print ghostscript-gnu make A4=yes BATCH=yes install
print a2ps-a4
print psutils-a4
print gv
print acroread5
print transfig
archivers zip
archivers unzip
java linux-sun-jdk13 yes | make install
java jdk13
www apache2
www weblint
www amaya
www mozilla make WITHOUT_MAILNEWS=yes WITHOUT_CHATZILLA=yes install
www netscape48-navigator
www checkbot
www privoxy
graphics xfig
graphics xv
graphics fxtv
lang expect
news tin
net freebsd-uucp
net cvsup-without-gui
net pathchar make NO_CHECKSUM=yes install
ftp wget
ftp ncftp3
textproc ispell
german ispell-neu
german ispell-alt
textproc docproj make JADETEX=yes HAVE_MOTIF=yes install
sysutils samefile
sysutils pstree
sysutils mkisofs
sysutils cdrtools
sysutils grub
devel ddd
devel ctags
devel ElectricFence
mail procmail make BATCH=yes install
mail metamail
mail mutt
mail spamoracle
emulators mtools
sysutils portupgrade
news inn-stable CONFIGURE_ARGS="--enable-uucp-rnews --enable-setgid-inews" make install
misc figlet-fonts
textproc gmat
EOF
while read CATEGORY NAME CMD; do
    case "${CATEGORY}" in
    \#*) continue;;
    '') continue;;
    esac
    DIR="${PORTS}/${CATEGORY}/${NAME}"
    if ! test -d "${DIR}"; then
        echo "$DIR does not exist -- ignored"
        continue
    fi
    cd ${DIR}
    PKGNAME=`make -V PKGNAME`
    if test -d "${DBDIR}/${PKGNAME}"; then
        echo "${CATEGORY}/${NAME} already installed as ${PKGNAME}"
        continue
    fi
    LOG="${LOGDIR}/${CATEGORY}+${NAME}"
    echo "===> Installing ${CATEGORY}/${NAME}; logging to ${LOG}"
    test -n "${CMD}" || CMD="make install"
    if test -n "${DRYRUN}"; then
        echo "${CMD}"
        continue
    fi
    date "++++++++++ %v %T +++++++++" > ${LOG}
    echo "CMD: ${CMD}" >> ${LOG}
    (
        make clean
        eval "${CMD}"
        # make clean # ${PORTS} 以下のディスク容量がすくなければコメントをはずす
    ) 2>&1 | tee -a ${LOG}
done

# StarOffice は、X11 を使った対話的インストールが必要なので、
# 古いシステム上で "make package" を実行して作成した
# package からインストールする。
#pkg_add ${PORTS}/editors/staroffice52/staroffice-*.tbz

# EOF $RCSfile: stage_2.sh,v $    vim: tabstop=4:

6. 第 3 段階

第 2 段階で、好みの ports がインストールされましたが、 ports には、設定を必要とするものがあります。 第 3 段階は、インストール後の設定を行なう段階です。 stage_2.sh の最後にこの段階を統合することもできたのですが、 わたしは port をインストールすることと初期設定を変更することが異なる工程であると考えたため、 独立した段階としています。

第 3 段階は、Makefile として実装しています。 これは、次のように実行することで、設定対象を簡単に選ぶことができるからです。

# make -f stage_3.mk target

stage_2.sh の段階で、 stage_3.mk を共有パーティションに置くか、 新しいシステムのどこかにコピーするなどして、 新しいシステムが起動した時に stage_3.mk が使えるようにしておきましょう。

# stage_3.mk - FreeBSD From Scratch, 第 3 段階: ports をインストールした後の設定
#              Usage: make -f stage_3.mk all     (すべての設定を行なう)
#                or   make -f stage_3.mk target  (target の設定を行なう)
#
# すべての target が、複数回実行しても悪影響をおよぼさないように
# 確認しておくとよい。
#
# $FreeBSD: doc/ja_JP.eucJP/articles/fbsd-from-scratch/stage_3.mk,v 1.1 2003/10/11 15:06:56 hrs Exp $
# Original revision: 1.1

.POSIX:

message:
    @echo "Please use one of the following targets:"
    @echo "config_apache"
    @echo "config_inn"
    @echo "config_javaplugin"
    @echo "config_privoxy"
    @echo "config_setiathome"
    @echo "config_sgml"
    @echo "config_sudo"
    @echo "config_TeX"
    @echo "config_tin"
    @echo "config_uucp"
    @echo "all -- all of the above"

all: config_apache \
    config_inn \
    config_javaplugin \
    config_privoxy \
    config_setiathome \
    config_sgml \
    config_sudo \
    config_TeX \
    config_tin \
    config_uucp

config_apache:
    # 1. httpd.conf の変更
    perl -pi \
    -e 's/#ServerName new.host.name/ServerName hal9000.s.shuttle.de/;' \
    -e 's/^ServerAdmin.*/ServerAdmin schweikh\@schweikhardt.net/;' \
    -e 's,/usr/local/www/cgi-bin/,/home/opt/www/cgi-bin/,;' \
      /usr/local/etc/apache2/httpd.conf
    # 2. ウェブページに対するシンボリックリンクの作成
    cd /usr/local/www/data; \
    ln -fs /home/schweikh/prj/homepage schweikhardt.net; \
    ln -fs /home/opt/www/test .

config_inn:
    pw usermod -n news -d /usr/local/news -s /bin/sh
    # ニュースシステムの初期設定
    cd /home/root/setup; \
    install -C -o news -g news -m 664 active newsgroups /usr/local/news/db
    # port の innd.sh は壊れていて、
    #存在しない history.pag をチェックしようとする。
    cd /home/root/setup; \
    install -C -o root -g wheel -m 555 innd.sh /usr/local/etc/rc.d
    # 格納方法の設定
    cd /home/root/setup;      \
    printf "%s\n%s\n%s\n%s\n" \
        "method tradspool {"  \
        "  newsgroups: *"     \
        "  class: 0"          \
        "}"                   \
    >storage.conf;            \
    install -C -o news -g news -m 664 storage.conf /usr/local/news/etc
    # newsfeeds の設定
    printf "%s\n%s\n" \
        "ME:*::"      \
        "shuttle/news2.shuttle.de:!junk,!control:B32768/512,Tf,Wfb:" \
    >/usr/local/news/etc/newsfeeds
    # inn.conf の設定
    perl -pi                                                   \
    -e 's/^(organization:\s*).*/$$1 An Open Pod Bay Door/;'    \
    -e 's/^(pathhost:\s*).*/$$1 hal9000.schweikhardt.net/;'    \
    -e 's/^(server:).*/$$1 localhost/;'                        \
    -e 's/^(domain:).*/$$1 schweikhardt.net/;'                 \
    -e 's/^(fromhost:).*/$$1 schweikhardt.net/;'               \
    -e 's,^(moderatormailer:).*,$$1 \%s\@moderators.isc.org,;' \
    -e 's,/usr/local/news/spool,/share/news/spool,;'           \
    /usr/local/news/etc/inn.conf

config_javaplugin:
    cd /usr/local/lib/netscape-linux/plugins; \
    if ! test -h javaplugin.so; then \
        ln -s ../../../linux-sun-jdk1.3.1/jre/plugin/i386/ns4/javaplugin.so; \
    fi; \
    ls -l javaplugin.so

config_privoxy:
    install -C -o root -g wheel -m 644 config /usr/local/etc/privoxy

config_setiathome:
    perl -pi \
    -e 's,^.*seti_wrkdir.*#,seti_wrkdir=/home/nobody/setiathome #,;' \
    /usr/local/etc/rc.setiathome.conf

config_sgml:
    cp -p /usr/local/share/gmat/sgml/ISO_8879-1986/entities/* \
          /usr/local/share/sgml/docbook/4.1

config_sudo:
    if ! grep -q schweikh /usr/local/etc/sudoers; then \
        echo 'schweikh ALL = (ALL) NOPASSWD: ALL' >> /usr/local/etc/sudoers; \
    fi

config_TeX:
    # textproc/docproj では、FreeBSD ハンドブックを JadeTeX で
    # タイプセットするには、次の値を設定するよう指示されている
    perl -pi                                   \
    -e 's/^% original texmf.cnf/% texmf.cnf/;' \
    -e 's/^(hash_extra\s*=).*/$$1 60000/;'     \
    -e 's/^(pool_size\s*=).*/$$1 1000000/;'    \
    -e 's/^(max_strings\s*=).*/$$1 70000/;'    \
    -e 's/^(save_size\s*=).*/$$1 10000/;'      \
    /usr/local/share/texmf/web2c/texmf.cnf

config_tin:
    # tin が設定したファイルを読むように設定
    printf "%s\n%s\n%s\n"                              \
        "activefile=/usr/local/news/db/active"         \
        "newsgroupsfile=/usr/local/news/db/newsgroups" \
        "spooldir=/share/news/spool/articles"          \
    >/usr/local/etc/tin.defaults

config_uucp:
    # UUCP が /usr/bin/rnews を見つけられるようにする
    cd /usr/bin; ln -fs ../local/news/bin/rnews .
    # 実際の UUCP の設定
    echo nodename js2015           > /usr/local/etc/uucp/config
    echo shuttle js2015 `cat uucp` > /usr/local/etc/uucp/call
    printf 'port tcp\ntype tcp\n'  > /usr/local/etc/uucp/port
    printf "%s\n%s\n%s\n%s\n%s\n%s\n%s\n" \
        "call-login    *"                 \
        "call-password *"                 \
        "time          any"               \
        "system        shuttle"           \
        "address       mail.s.shuttle.de" \
        "commands      rmail rnews"       \
        "port          tcp"               \
    >/usr/local/etc/uucp/sys
    cd /usr/local/etc/uucp; chown uucp:uucp *; chmod o-rwx *
    # 起動後に uucico を実行する
    mkdir -p /usr/local/etc/rc.d; cp uucp.sh /usr/local/etc/rc.d

# EOF $RCSfile: stage_3.mk,v $    vim: tabstop=4:

7. 制限事項

対話的で、かつ make BATCH=YES install でのインストールに対応していない port の自動インストールは難しいかも知れません。 対話的にインストールする ports には、ライセンス条項の同意を尋ねられた時に yes と入力するだけのものがいくつかあります。 そのように入力が標準入力から読みとられる場合は、 適切な回答をインストールコマンド (通常は make install) にパイプで渡すことができます (stage_2.shjava/linux-sun-jdk13 でとった方法がそうです)。

しかしこの方法は、たとえば editors/staroffice52 の場合にはうまく動きません。 これは X11 が実行されていることを要求するからです。 インストール手順には多くのクリックや文字入力が必要なので、 他の ports のように自動化することはできません。 わたしは、次のようにして問題を回避しました。 最初に古いシステムで staroffice の package を作成し、

# cd /usr/ports/editors/staroffice52
# make package
===>  Building package for staroffice-5.2_1
Creating package /usr/ports/editors/staroffice52/staroffice-5.2_1.tbz
Registering depends:.
Creating bzip'd tar ball in '/usr/ports/editors/staroffice52/staroffice-5.2_1.tbz'

その後、第 2 段階で次のようにしたわけです。

# pkg_add /usr/ports/editors/staroffice52/staroffice-5.2_1.tbz

その他に、設定ファイルのアップグレード問題に気をつける必要があります。 一般的に、設定ファイルの書式や内容がいつ変更されるかを知ることはできません。 新しいグループが /etc/group に追加されるかも知れませんし、/etc/passwd に新しいフィールドが追加されるかも知れません。 このような例は、実際に過去にありました。 単純に古いシステムから新しいシステムに設定ファイルをコピーするだけで ほとんどの場合は十分なのですが、時には不都合な場合もあります。 古いファイルを上書きする方法でシステムをアップグレードしたら、 ローカルにある設定ファイルに新しく追加されたかも知れない項目を統合する目的で mergemaster を使うと思います。 しかし残念なことに、mergemaster はベースシステムに存在するファイルだけで、インストールした ports については何も処理を行なってくれません。 サードパーティ製ソフトウェアには、 リリースのたびに設定ファイルのフォーマットが変更され、 わたしをイライラさせるようなものもあります。 注意すること以外にできることはありませんが、 特にメジャーバージョンがあがった時は気を付けてください。 わたしは以前、ウェブサーバ、 ニュースサーバ、ニュースリーダのファイルを書き換えたり、 書き直すはめになったことがあります。 活発に開発が進められているソフトウェアはすべて、 設定ファイルの書式が変更されていないか確認しておきましょう。

わたしは 5-CURRENT から 5-CURRENT に更新するために 「FreeBSD をゼロから設定する」方法を数回使いましたが、 4-STABLE5-CURRENT の間で更新を行なった経験はありません。 異なるメジャーリリース番号の間は、非常の多数の変更が行なわれているため、 更新作業はもっと複雑なものになると思います。 (試したわけではないのですが) 4-STABLE から 4-STABLE への更新であれば、「FreeBSD をゼロから設定する」方法は問題なく動作するはずです。 4-STABLE のユーザは、次の点を考慮してください。

Note: デバイスファイルシステム (devfs(5)) を使ってなければ、 第 1 段階のステップ 6 で MAKEDEV(8) を使い、 ハードウェア用のデバイスファイルを作成するとよいでしょう。