发新话题
打印

OpenBSD LiveCD制作过程

OpenBSD LiveCD制作过程

2005-3-31更新

OpenBSD LiveCD制作过程
    刚开始接触OpenBSD时,我就很想使用OpenBSD LiveCD,但没找到。今年1月那时起就想自己做一个LiveCD,一个新年一过,就将它放下了,现在又重新开始制作,今天写这篇文章只是我思路的一个开始,制作的LiveCD,还不完善,我会后续为其改进。

    制作过程:
一、        准备工作:
1、        一个能运行OpenBSD系统的所需要的必备文件,这里我就没像一些mini LiveCD那样对系统进行剔牛肉进行精减了,只是将一个能运行的系统的所有文件进行备份。而且如果备份的文件不大, 那制作出的LiveCD就很小,我们就可以将这个备份的系统文件也一起放进CD中,到时就可以用这个LiveCD安装系统,方法就是我今年1月3日写的《OpenBSD  另类安装法》。
2、        OpenBSD系统的源程序,我们在制作过程中需要编译光盘镜像。

二、        将备份的系统文件解开到一个目录下,我将以这个目录作为LiveCD的根目录制作CD,如/usr/live/。
三、        将OpenBSD源程序解开到/usr/src下,然后:
1、        安装必须的程序:
# cd /usr/src/distrib/crunch/crunchgen
#make; make install
#cd /usr/src/distrib/crunch/crunchide
#make; make install

2、        定制内核
内核文件是/usr/src/sys/arch/i386/conf/RAMDISK_CD,我们需要对这个文件进行修改,其中有三行最重要,一定不能少:
option  MFS
option  UNION
config  bsd root on cd0a
LiveCD运行于MFS上,而且它的根目录在CD上。

还有其它一些选项如:
option SMALL_KERNEL
option NO_PROPLICE
option TIMEZONE=0
option DST=0
option RAMDISK_HOOKS
option MINIROOTSIZE=3560

需要将一些多余的东西删除,因此这个内核大小控制在2.88M以内,太大时会编译失败。我的RAMDISK_CD是将/usr/src/sys/arch/i386/conf/GENERIC 和/usr/src/sys/conf/GENERIC这两个文章合并然后去掉一些内容整理出来的。

3、        编译内核
# cd /usr/src/distrib/i386/ramdisk_cd
#make
成功后会在目录下生成cdrom36.fs这个文件,我们将这个文件复制到制作LiveCD的目录下/usr/live/。

四、        修改/usr/live/etc中的各类文件
1、fstab

[Copy to clipboard] [ - ]
CODE:
/dev/cd0a / cd9660 ro,noatime 0 0
swap        /dev        mfs        rw,noatime,union,-s=16384        0 0
swap        /tmp        mfs        rw,nodev,noexec,nosuid,noatime,-s=32768        0 0
swap        /etc        mfs        rw,noatime,-s=16384        0 0
swap        /var        mfs        rw,noatime,-s=16384        0 0
swap        /home mfs        rw,noatime,-s=16384        0 0

以前我没发现fstab的功能居然如此强大, 以为它只能mount已分好的挂载点, 原来它在光盘系统中还可以在启动时自动挂载写入fstab中的mfs.

2、rc
   OpenBSD启动时首先读取这个文件, 如果找不到它就会读取根目录下的/.profile文件进行初始配置。
   修改/etc/rc文件, 三个地方修改

[Copy to clipboard] [ - ]
CODE:
   
#        $OpenBSD: rc,v 1.251 2004/08/21 08:17:28 hshoexer Exp $

# System startup script run by init on autoboot
# or after single-user.
# Output and error are redirected to console by init,
# and the console is the controlling terminal.

# Subroutines (have to come first).

# Strip comments (and leading/trailing whitespace if IFS is set)
# from a file and spew to stdout

stripcom() {
        local _file="$1"
        local _line

        {
                while read _line ; do
                        _line=${_line%%#*}                # strip comments
                        test -z "$_line" && continue
                        echo $_line
                done
        } < $_file
}

# End subroutines

stty status '^T'

# Set shell to ignore SIGINT (2), but not children;
# shell catches SIGQUIT (3) and returns to single user after fsck.
trap : 2
trap : 3        # shouldn't be needed

HOME=/; export HOME
PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH

if [ $1x = shutdownx ]; then
        dd if=/dev/urandom of=/var/db/host.random bs=1024 count=64 >;/dev/null 2>;&1
        chmod 600 /var/db/host.random >;/dev/null 2>;&1
        if [ $? -eq 0 -a -f /etc/rc.shutdown ]; then
                echo /etc/rc.shutdown in progress...
                . /etc/rc.shutdown
                echo /etc/rc.shutdown complete.

                # bring carp interfaces down gracefully
                for hn in /etc/hostname.carp[0-9]*; do
                        # Strip off /etc/hostname. prefix
                        if=${hn#/etc/hostname.}
                        test "$if" = "carp[0-9]*" && continue

                        ifconfig $if >; /dev/null 2>;&1
                        if [ "$?" != "0" ]; then
                                ifconfig $if down
                        fi
                done

                if [ "X${powerdown}" = X"YES" ]; then
                        exit 2
                fi

        else
                echo single user: not running /etc/rc.shutdown
        fi
        exit 0
fi

# Configure ccd devices.
if [ -f /etc/ccd.conf ]; then
        ccdconfig -C
fi

# Configure raid devices.
for dev in 0 1 2 3; do
        if [ -f /etc/raid$dev.conf ]; then
                raidctl -c /etc/raid$dev.conf raid$dev
        fi
done

# Check parity on raid devices.
raidctl -P all

swapctl -A -t blk

if [ -e /fastboot ]; then
        echo "Fast boot: skipping disk checks."
elif [ $1x = autobootx ]; then
        echo "Automatic boot in progress: starting file system checks."
#        fsck –p      这一行要注释掉
        case $? in
        0)
                ;;
        2)
                exit 1
                ;;
        4)
                echo "Rebooting..."
                reboot
                echo "Reboot failed; help!"
                exit 1
                ;;
        8)
                echo "Automatic file system check failed; help!"
                exit 1
                ;;
        12)
                echo "Boot interrupted."
                exit 1
                ;;
        130)
                # interrupt before catcher installed
                exit 1
                ;;
        *)
                echo "Unknown error; help!"
                exit 1
                ;;
        esac
fi

trap "echo 'Boot interrupted.'; exit 1" 3

umount -a >;/dev/null 2>;&1
mount -a -t nonfs
mount -uw /                # root on nfs requires this, others aren't hurt
rm -f /fastboot                # XXX (root now writeable)

这里需要在/dev下建立三个设备点, 不然后一步tar出错, 系统启动时会问题多多
mknod /dev/stdout c 22 1
mknod /dev/stdin c 22 0
mknod /dev/stderr c 22 2

if [ -f /mfs/mfs.tgz ]; then
        tar zxpf /mfs/mfs.tgz -C /
        echo 'Fixed up mfs from /mfs/mfs.tgz'
fi
chmod 755 /dev /etc /var
chmod a+rwx,a+t /tmp

# set flags on ttys.  (do early, in case they use tty for SLIP in netstart)
echo 'setting tty flags'
ttyflags -a

if [ "X${pf}" != X"NO" ]; then
        RULES="block all"
        RULES="$RULES\npass on lo0"
        RULES="$RULES\npass in proto tcp from any to any port 22 keep state"
        RULES="$RULES\npass out proto { tcp, udp } from any to any port 53 keep state"
        RULES="$RULES\npass out inet proto icmp all icmp-type echoreq keep state"
        RULES="$RULES\npass out inet6 proto icmp6 all icmp6-type routersol"
        RULES="$RULES\npass in inet6 proto icmp6 all icmp6-type routeradv"
        RULES="$RULES\npass proto { pfsync, carp }"
        case `sysctl vfs.mounts.nfs 2>;/dev/null` in
        *[1-9]*)
                # don't kill NFS
                RULES="scrub in all no-df\n$RULES"
                RULES="$RULES\npass in proto udp from any port { 111, 2049 } to any"
                RULES="$RULES\npass out proto udp from any to any port { 111, 2049 }"
                ;;
        esac
        echo $RULES | pfctl -f - -e
fi

if [ -f /etc/sysctl.conf ]; then
(
        # delete comments and blank lines
        set -- `stripcom /etc/sysctl.conf`
        while [ $# -ge 1 ] ; do
                sysctl $1
                shift
        done
)
fi

# set hostname, turn on network
echo "Setting Network......"     修改这里, 加入我写的setnetwork脚本,
. /etc/setnetwork                     这样网络设置在每台机上都可以重新设置而不是更改文件.

3、上一步最后有一个setnetwork脚本,这一步写出。我的脚本编写不会, 只好从其它地方抄过来,有一些错误,需要请高手改写。这个文件有点大, 贴在最后吧!

4、将一些网络配置的文件删除如:hosts, mygate等。上一步的network在启动时会生成这些配置文件。

五、        备份几个目录,在4.3 rc脚本中的MFS部分那个文件。
在/usr/live/ LiveCD根目录下建立文件夹:mfs 然后:
# tar cvzfp mfs/mfs.tgz dev etc home var
  
六、        制作ISO镜像:
# cd /usr/live
# mkhybrid –b cdrom36.fs –c boot.catalog –R –v –o /usr/OpenBSD-LiveCD.iso /usr/live

现在制作完成, 你可以用虚拟机测试这个生成的ISO镜像。
参考了网上许多文章, 这里就不一一列出来了, 感谢那些大哥写出那么好的教程.

我现在还有几个问题需要解决,主要是MFS这方面的,另外shell脚本的错误也需要大家来解决。
设置网络用的setnetwork脚本:
setnetwork:

[Copy to clipboard] [ - ]
CODE:
#!/bin/sh

get_dkdevs() {
        bsort `sed -ne "${MDDISKDEVS:-/^[sw]d[0-9][0-9]* /s/ .*//p}" /var/run/dmesg.boot`
}

get_cddevs() {
        bsort `sed -ne "${MDCDDEVS:-/^cd[0-9][0-9]* /s/ .*//p}" /var/run/dmesg.boot`
}

get_ifdevs() {
        ifconfig -a \
            | egrep -v '^[[:space:]]|(bridge|enc|gif|gre|lo|pflog|pfsync|ppp|sl|tun|vlan)[[:digit:]]+:' \
            | sed -ne 's/^\(.*\):.*/\1/p'
}

askpass() {
        set -o noglob
        stty -echo
        read resp?"$1 "
        stty echo
        set +o noglob
        echo
}

ask() {
        local _question=$1 _default=$2

        set -o noglob
        while : ; do
                echo -n "$_question "
                [[ -z $_default ]] || echo -n "[$_default] "
                read resp
                case $resp in
                !)        echo "Type 'exit' to return to install."
                        sh
                        ;;
                !*)        eval ${resp#?}
                        ;;
                *)        : ${resp:=$_default}
                        break
                        ;;
                esac
        done
        set +o noglob
}

ask_until() {
        resp=
        while [[ -z $resp ]] ; do
                ask "$1" "$2"
        done
}

ask_yn() {
        local _q=$1 _a=${2:-no} _resp
        typeset -l _resp

        while : ; do
                ask "$_q" "$_a"
                _resp=$resp
                case $_resp in
                y|yes)        resp=y ; return ;;
                n|no)        resp=n ; return ;;
                esac
        done
}

ask_which() {
        local _name=$1 _query=$2 _devs=$3 _defdev=$4 _err=$5

        set -- $_devs
        if [[ $# -lt 1 ]]; then
                echo "${_err:=No ${_name}s found}."
                resp=done
                return
        fi
        : ${_defdev:=$1}

        _devs="$*"

        while : ; do
                ask "Available ${_name}s are: ${_devs}.\nWhich one ${_query}? (or 'done')" "$_defdev"
                [[ $resp == done ]] && break

                if isin "$resp" $_devs; then
                        makedev $resp && break
                else
                        echo "'$resp' is not a valid choice."
                fi
        done
}

isin() {
        local        _a=$1 _b

        shift
        for _b; do
                [ "$_a" = "$_b" ] && return 0
        done
        return 1
}

addel() {
        local        _a=$1

        shift

        echo -n "$*"
        isin "$_a" $* || echo -n " $_a"
}

rmel() {
        local        _a=$1 _b

        shift
        for _b; do
                [ "$_a" != "$_b" ] && echo -n "$_b "
        done
}

edit_tmp_file() {
        local _file=$1

        ask_yn "Edit $_file with $EDITOR?"
        [[ $resp == y ]] && $EDITOR /tmp/$_file
}

manual_net_cfg() {
        ask_yn "Do you want to do any manual network configuration?"

        [[ $resp == y ]] && { echo "Type 'exit' to return to $MODE." ; sh ; }
}

makedev() {
        local _dev=$1 _node=/dev/r${1}c

        if isin $_dev $IFDEVS || [[ -c $_node || -z ${_dev##+([0-9])} ]] ; then
                return 0
        fi

        if [[ ! -r /dev/MAKEDEV ]] ; then
                echo "No /dev/MAKEDEV. Can't create device nodes for ${_dev}."
                return 1
        fi

        (cd /dev; sh MAKEDEV $_dev)

        [[ -c $_node ]] || return 1

        DEVSMADE=`addel $_dev $DEVSMADE`
}

addhostent() {
        sed "/ $2\$/d" /etc/hosts >; /etc/hosts.new
        mv /etc/hosts.new /etc/hosts

        echo "$1 $2" >;>; /etc/hosts
}

configure_ifs() {
        local _IFDEVS=$IFDEVS _ifs _name _media _hn

        while : ; do
                ask_which "interface" "do you wish to initialize" "$_IFDEVS" \
                        "" "No more interfaces to initialize"
                [[ $resp == done ]] && break

                _ifs=$resp
                _hn=/etc/hostname.$_ifs

                ask "Symbolic (host) name for $_ifs?" "$(hostname -s)"
                _name=$resp

                _media=$(ifconfig -m $_ifs | grep "media ")
                if [[ -n $_media ]]; then
                        cat << __EOT
The media options for $_ifs are currently
$(ifconfig -m $_ifs | sed -n '/supported/D;/media:/p')
__EOT
                        ask_yn "Do you want to change the media options?"
                        case $resp in
                        y)        cat << __EOT
Supported media options for $_ifs are:
$_media
__EOT
                                ask "Media options for $_ifs?"
                                _media=$resp
                                ifconfig $_ifs $_media || return 1
                                ;;
                        n)        _media=
                                ;;
                        esac
                fi

                rm -f $_hn
                v4_config "$_ifs" "$_media" "$_name" "$_hn"

                [[ -f $_hn ]] && _IFDEVS=$(rmel "$_ifs" $_IFDEVS)
        done
}

v4_info() {
        ifconfig $1 inet | sed -n '
                1s/.*<UP,.*/UP/p
                1s/.*<.*/DOWN/p
                /inet/s/netmask//
                /inet/s///p'
}

dhcp_request() {
        local _ifs=$1 _hostname=$2

        echo "initial-interval 1;" >; /etc/dhclient.conf

        if [[ -n $_hostname ]]; then
                echo "send host-name \"$_hostname\";" >;>; /etc/dhclient.conf
                echo "Issuing hostname-associated DHCP request for $_ifs."
        else
                echo "Issuing free-roaming DHCP request for $_ifs."
        fi

        cat >;>; /etc/dhclient.conf << __EOT
request subnet-mask,
        broadcast-address,
        routers,
        domain-name,
        domain-name-servers,
        host-name;
__EOT

        cat >;>; /etc/resolv.conf.tail << __EOT
lookup file bind
__EOT

        dhclient $_ifs

        set -- $(v4_info $_ifs)

        if [[ $1 == UP && $2 == "0.0.0.0" ]]; then
                ifconfig $_ifs delete down
                rm /etc/dhclient.conf /etc/resolv.conf.tail
                return 1
        fi

#        cp /etc/dhclient.conf /tmp/dhclient.conf
#        cp /etc/resolv.conf.tail /tmp/resolv.conf.tail

        return 0
}

v4_config() {
        local _ifs=$1 _media=$2 _name=$3 _hn=$4 _prompt

        set -- $(v4_info $_ifs)
        if [[ -n $2 ]]; then
                ifconfig $_ifs inet $2 delete
                [[ $2 != "0.0.0.0" ]] && { _addr=$2; _mask=$3; }
        fi

        [[ -x /sbin/dhclient ]] && _prompt=" or 'dhcp'"
        _prompt="IPv4 address for $_ifs? (or 'none'$_prompt)"

        ask_until "$_prompt" "$_addr"
        case $resp in
        none)        ;;
        dhcp)        if [[ ! -x /sbin/dhclient ]]; then
                        echo "DHCP not possible - no /sbin/dhclient."
                elif dhcp_request $_ifs "$_name" || dhcp_request $_ifs ; then
                        addhostent "127.0.0.1" "$_name"
                        echo "dhcp NONE NONE NONE $_media" >; $_hn
                fi
                ;;
        *)        _addr=$resp
                ask_until "Netmask?" "${_mask:=255.255.255.0}"
                if ifconfig $_ifs inet $_addr netmask $resp up ; then
                        addhostent "$_addr" "$_name"
                        echo "inet $_addr $resp NONE $_media" >; $_hn
                fi
                ;;
        esac
}

v4_defroute() {
        local _dr _prompt=" or 'none'"

        [[ -x /sbin/dhclient ]] && _prompt=", 'dhcp'$_prompt"
        _prompt="Default IPv4 route? (IPv4 address$_prompt)"

        _dr=$(route -n show -inet | sed -ne '/^default */{s///; s/ .*//; p;}')
        [[ -f /tmp/dhclient.conf ]] && _dr=dhcp

        while : ; do
                ask_until "$_prompt" "$_dr"
                case $resp in
                none|dhcp) break ;;
                esac
                route delete -inet default >; /dev/null 2>;&1
                route -n add -inet -host default "$resp" && { echo "$resp" >;/etc/mygate ; break ; }
                route -n add -inet -host default $_dr >;/dev/null 2>;&1
        done
}

isalphanumeric() {
        local _n=$1
        while [[ ${#_n} -ne 0 ]]; do
                case $_n in
                [A-Za-z0-9]*)        ;;
                *)                return 1;;
                esac
                _n=${_n#?}
        done
        return 0
}

enable_network() {
        local _netfile

#        for _netfile in hosts dhclient.conf resolv.conf resolv.conf.tail protocols services; do
#                if [ -f /mnt/etc/${_netfile} ]; then
#                        cp /mnt/etc/${_netfile} /etc/${_netfile}
#                fi
#        done

        ifconfig lo0 inet 127.0.0.1

        for hn in /etc/hostname.*; do
                if=${hn#/etc/hostname.}

                if ! isalphanumeric "$if"; then
                        continue
                fi
                ifconfig $if >; /dev/null 2>;&1
                if [ $? -ne 0 ]; then
                        continue
                fi

                while :; do
                        if [ "$cmd2" ]; then
                                set -- $cmd2
                                af=$1 name=$2 mask=$3 bcaddr=$4 ext1=$5 cmd2=
                                i=1; while [ i -lt 6 -a -n "$1" ]; do shift; let i=i+1; done
                                ext2="$@"
                        else
                                read af name mask bcaddr ext1 ext2 || break
                        fi
                        case $af in
                        "#"*|"!"*|"bridge"|""|"rtsol")
                                continue
                                ;;
                        "dhcp")        [ "$name" = "NONE" ] && name=
                                [ "$mask" = "NONE" ] && mask=
                                [ "$bcaddr" = "NONE" ] && bcaddr=
                                ifconfig $if $name $mask $bcaddr $ext1 $ext2 down
                                cmd="dhclient $if"
                                ;;
                        "up")
                                cmd="ifconfig $if $name $mask $bcaddr $ext1 $ext2 up"
                                ;;
                        *)        read dt dtaddr
                                if [ "$name" = "alias" ]; then
                                        alias=$name
                                        name=$mask
                                        mask=$bcaddr
                                        bcaddr=$ext1
                                        ext1=$ext2
                                        ext2=
                                else
                                        alias=
                                fi
                                cmd="ifconfig $if $af $alias $name "
                                case $dt in
                                dest)        cmd="$cmd $dtaddr"
                                        ;;
                                [a-z!]*)
                                        cmd2="$dt $dtaddr"
                                        ;;
                                esac
                                if [ ! -n "$name" ]; then
                                        echo "/etc/hostname.$if: invalid network configuration file"
                                        return
                                fi
                                case $af in
                                inet)        [ "$mask" ] && cmd="$cmd netmask $mask"
                                        if [ "$bcaddr" -a "$bcaddr" != "NONE" ]; then
                                                cmd="$cmd broadcast $bcaddr"
                                        fi
                                        [ "$alias" ] && rtcmd="; route -qn add -host $name 127.0.0.1"
                                        ;;
                                inet6)
                                        continue
                                        ;;
                                *)        cmd="$cmd $mask $bcaddr"
                                esac
                                cmd="$cmd $ext1 $ext2$rtcmd" rtcmd=
                                ;;
                        esac
                        eval "$cmd"
                done </etc/hostname.$if
        done

        if [ -f /etc/mygate ]; then
                route delete default >;/dev/null 2>;&1
                route -qn add -host default $(< /etc/mygate)
        fi

        route -qn add -host `hostname` 127.0.0.1 >;/dev/null
        route -qn add -net 127 127.0.0.1 -reject >;/dev/null

        echo "Network interface configuration:"
        ifconfig -am

        route -n show
        if [ -f /etc/resolv.conf ]; then
                echo "\nResolver enabled."
        else
                echo "\nResolver not enabled."
        fi
}

get_fqdn() {
        local _dn

        _dn=$(hostname)
        _dn=${_dn#$(hostname -s)}
        _dn=${_dn#.}

        echo "${_dn:=my.domain}"
}

donetconfig() {
        local _dn _ns

        configure_ifs

        if [ -f /etc/resolv.conf.shadow ]; then
                mv /etc/resolv.conf.shadow /etc/resolv.conf
                _ns=$(sed -ne '/^nameserver /s///p' /etc/resolv.conf)
                _dn=$(sed -n \
                        -e '/^domain[[:space:]][[:space:]]*/{s///;s/\([^[:space:]]*\).*$/\1/;h;}' \
                        -e '/^search[[:space:]][[:space:]]*/{s///;s/\([^[:space:]]*\).*$/\1/;h;}' \
                        -e '${g;p;}' /tmp/resolv.conf)
        fi

        ask "DNS domain name? (e.g. 'bar.com')" "${_dn:=$(get_fqdn)}"
        hostname "$(hostname -s).$resp"

        ask "DNS nameserver? (IP address or 'none')" "${_ns:=none}"
        if [[ $resp != none ]]; then
                echo "lookup file bind" >; /etc/resolv.conf
                for _ns in $resp; do
                        echo "nameserver $_ns" >;>; /etc/resolv.conf
                done
                ask_yn "Use the nameserver now?" yes
                [[ $resp == y ]] && cp /etc/resolv.conf /etc/resolv.conf.shadow
        fi

        [[ -n $(ifconfig -a | sed -ne '/[         ]inet .* broadcast /p') ]] && v4_defroute

#        edit_tmp_file hosts
#        manual_net_cfg
}

IFDEVS=$(get_ifdevs)
MODE=OpenBSD-LiveCD
EDITOR=mg

ask_until "\nSystem hostname? (short form, e.g. 'foo')" "$(hostname -s)"
[[ ${resp%%.*} != $(hostname -s) ]] && hostname $resp

( cd /etc; rm -f host* my* resolv.* dhclient.* )

cat >; /etc/hosts << __EOT
::1 localhost
127.0.0.1 localhost
::1 $(hostname -s)
127.0.0.1 $(hostname -s)
__EOT

ask_yn "Configure the network?" yes
[[ $resp == y ]] && donetconfig

( cd /etc
hostname >; myname

_dn=$(get_fqdn)
while read _addr _hn _aliases; do
        if [[ -n $_aliases || $_hn != ${_hn%%.*} || -z $_dn ]]; then
                echo "$_addr $_hn $_aliases"
        else
                echo "$_addr $_hn.$_dn $_hn"
        fi
done < hosts >; hosts.new
mv hosts.new hosts
)

ask "HTTP/FTP proxy URL? (e.g. 'http://proxy:8080', or 'none')" \
            "${ftp_proxy:-none}"
unset ftp_proxy http_proxy
[[ $resp == none ]] || export ftp_proxy=$resp http_proxy=$resp

我将制作的一个livecd放在:
ftp://ftp.linuxeden.com/rianren/
各位可以下载来试试!

TOP

发新话题