rc.initdiskless revision 127657
1118611Snjl#!/bin/sh
2118611Snjl#
3118611Snjl# Copyright (c) 1999  Matt Dillon
4118611Snjl# All rights reserved.
5138287Smarks#
6118611Snjl# Redistribution and use in source and binary forms, with or without
7118611Snjl# modification, are permitted provided that the following conditions
8118611Snjl# are met:
9118611Snjl# 1. Redistributions of source code must retain the above copyright
10118611Snjl#    notice, this list of conditions and the following disclaimer.
11118611Snjl# 2. Redistributions in binary form must reproduce the above copyright
12118611Snjl#    notice, this list of conditions and the following disclaimer in the
13126372Snjl#    documentation and/or other materials provided with the distribution.
14118611Snjl#
15118611Snjl# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16118611Snjl# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17118611Snjl# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18118611Snjl# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19118611Snjl# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20118611Snjl# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21118611Snjl# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22118611Snjl# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23118611Snjl# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24118611Snjl# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25118611Snjl# SUCH DAMAGE.
26118611Snjl#
27118611Snjl# $FreeBSD: head/etc/rc.initdiskless 127657 2004-03-31 07:24:15Z luigi $
28118611Snjl#
29118611Snjl# PROVIDE: initdiskless
30118611Snjl# KEYWORD: FreeBSD nojail
31118611Snjl
32118611Snjl
33118611Snjl# On entry to this script the entire system consists of a read-only root
34118611Snjl# mounted via NFS.  We use the contents of /conf to create and populate
35118611Snjl# memory filesystems.  The kernel has run BOOTP and configured an interface
36118611Snjl# (otherwise it would not have been able to mount the NFS root!)
37118611Snjl#
38118611Snjl# The following directories are scanned.  Each sucessive directory overrides
39118611Snjl# (is merged into) the previous one.
40118611Snjl#
41118611Snjl#	/conf/base		universal base
42118611Snjl#	/conf/default		modified by a secondary universal base
43118611Snjl#	/conf/${ipba}		modified based on the assigned broadcast IP
44118611Snjl#	/conf/${ip}		modified based on the machine's assigned IP
45118611Snjl#
46118611Snjl# Each of these directories may contain any number of subdirectories which
47118611Snjl# represent directories in / on the diskless machine.  The existance of
48118611Snjl# these subdirectories causes this script to create a MEMORY FILESYSTEM for
49118611Snjl# /<sub_directory_name>.  For example, if /conf/base/etc exists then a
50118611Snjl# memory filesystem will be created for /etc.
51118611Snjl#
52118611Snjl# If a subdirectory contains the file 'remount' the contents of the file
53118611Snjl# is a mount command used to remount the subdirectory prior to it being
54118611Snjl# copied.  An example contents could be: "mount -o ro /dev/ad0s3".  Note
55118611Snjl# that the directory to be mounted on is supplied by this script.
56118611Snjl#
57118611Snjl# If a subdirectory contains the file 'diskless_remount' the contents of
58118611Snjl# the file is used to remount the subdirectory prior to it being copied to
59118611Snjl# the memory filesystem.  For example, if /conf/base/etc/diskless_remount
60118611Snjl# contains the string 'my.server.com:/etc' then my.server.com:/etc will be
61118611Snjl# mounted in place of the subdirectory.  This allows you to avoid making
62118611Snjl# duplicates of system directories in /conf.  Special processing is done
63118611Snjl# to allow specifications relative to the root filesystem.
64118611Snjl#
65118611Snjl# If a subdirectory contains the file 'md_size', the contents of the
66118611Snjl# file is used to determine the size of the memory filesystem, in 512
67118611Snjl# byte sectors.  The default is 10240 (5MB).  You only have to specify an
68118611Snjl# md_size if the default doesn't work for you (i.e. if it is too big or
69118611Snjl# too small).  For example, /conf/base/etc/md_size might contain '16384'.
70118611Snjl#
71118611Snjl# If /conf/<special_dir>/SUBDIR.cpio.gz exists, the file is cpio'd into
72118611Snjl# the specified /SUBDIR (and a memory filesystem is created for /SUBDIR
73118611Snjl# if necessary).
74118611Snjl#
75118611Snjl# If /conf/<special_dir>/SUBDIR.remove exists, the file contains a list
76118611Snjl# of paths which are rm -rf'd relative to /SUBDIR.
77118611Snjl#
78118611Snjl# You will almost universally want to create a /conf/base/etc containing
79118611Snjl# a diskless_remount and possibly an md_size file.  You will then almost
80118611Snjl# universally want to override rc.conf, rc.local, and fstab by creating
81118611Snjl# /conf/default/etc/{rc.conf,rc.local,fstab}.  Your fstab should be sure
82118611Snjl# to mount a /usr... typically an NFS readonly /usr.
83118611Snjl#
84118611Snjl# NOTE!  /var, /tmp, and /dev will be created elsewhere.
85118611Snjl# Those filesystems should not be specified in /conf.
86118611Snjl
87118611Snjldlv=`/sbin/sysctl -n vfs.nfs.diskless_valid 2> /dev/null`
88118611Snjl[ ${dlv:=0} -eq 0 ] && [ ! -f /etc/diskless ] && exit 0
89118611Snjl
90118611Snjl# chkerr:
91118611Snjl#
92118611Snjl# Routine to check for error
93118611Snjl#
94118611Snjl#	checks error code and drops into shell on failure.
95118611Snjl#	if shell exits, terminates script as well as /etc/rc.
96118611Snjl#
97118611Snjlchkerr() {
98118611Snjl    case $1 in
99118611Snjl    0)
100118611Snjl	;;
101118611Snjl    *)
102118611Snjl	echo "$2 failed: dropping into /bin/sh"
103118611Snjl	/bin/sh
104118611Snjl	# RESUME
105118611Snjl	;;
106118611Snjl    esac
107118611Snjl}
108118611Snjl
109118611Snjl# Create a generic memory disk
110118611Snjl#
111118611Snjlmount_md() {
112118611Snjl    /sbin/mdmfs -i 4096 -s $1 -M md $2
113118611Snjl}
114118611Snjl
115118611Snjl# Create the memory filesystem if it has not already been created
116118611Snjl#
117118611Snjlcreate_md() {
118118611Snjl    if [ "x`eval echo \\$md_created_$1`" = "x" ]; then
119118611Snjl	if [ "x`eval echo \\$md_size_$1`" = "x" ]; then
120118611Snjl	    md_size=10240
121118611Snjl	else
122118611Snjl	    md_size=`eval echo \\$md_size_$1`
123118611Snjl	fi
124118611Snjl	mount_md $md_size /$1
125118611Snjl	/bin/chmod 755 /$1
126118611Snjl	eval md_created_$1=created
127118611Snjl    fi
128118611Snjl}
129118611Snjl
130118611Snjl# DEBUGGING
131118611Snjl#
132118611Snjl# set -v
133118611Snjl
134138287Smarks# Figure out our interface and IP.
135118611Snjl#
136118611Snjlbootp_ifc=""
137118611Snjlbootp_ipa=""
138118611Snjlbootp_ipbca=""
139118611Snjlif [ ${dlv:=0} -ne 0 ] ; then
140118611Snjl	iflist=`ifconfig -l`
141118611Snjl	for i in ${iflist} ; do
142118611Snjl	    set `ifconfig ${i}`
143118611Snjl	    while [ $# -ge 1 ] ; do
144118611Snjl		if [ "${bootp_ifc}" = "" -a "$1" = "inet" ] ; then
145118611Snjl		    bootp_ifc=${i} ; bootp_ipa=${2} ; shift
146138287Smarks		fi
147118611Snjl		if [ "${bootp_ipbca}" = "" -a "$1" = "broadcast" ] ; then
148118611Snjl		    bootp_ipbca=$2; shift
149118611Snjl		fi
150118611Snjl		shift
151118611Snjl	    done
152118611Snjl	    if [ "${bootp_ifc}" != "" ] ; then
153118611Snjl		break
154118611Snjl	    fi
155138287Smarks	done
156138287Smarks	# Insert the directories passed with the T134 bootp cookie
157138287Smarks	# in the list of paths used for templates.
158138287Smarks	i="`/sbin/sysctl -n kern.bootp_cookie`"
159138287Smarks	[ "${i}" != "" ] && bootp_ipbca="${bootp_ipbca} ${i}"
160138287Smarks
161138287Smarks	echo "Interface ${bootp_ifc} IP-Address ${bootp_ipa} Broadcast ${bootp_ipbca}"
162138287Smarksfi
163138287Smarks
164138287Smarks# Figure out our NFS root path
165138287Smarks#
166138287Smarksset `mount -t nfs`
167138287Smarkswhile [ $# -ge 1 ] ; do
168118611Snjl    if [ "$2" = "on" -a "$3" = "/" ]; then
169118611Snjl	nfsroot="$1"
170138287Smarks	break
171118611Snjl    fi
172118611Snjl    shift
173118611Snjldone
174118611Snjl
175138287Smarks# The list of directories with template files
176118611Snjltemplates="base default ${bootp_ipbca} ${bootp_ipa}"
177118611Snjl
178118611Snjl# The list of filesystems to umount after the copy
179118611Snjlto_umount=""
180118611Snjl
181118611Snjl# If /conf/diskless_remount exists, remount all of /conf.  This allows
182118611Snjl# multiple roots to share the same conf files.
183118611Snjlif [ -d /conf -a -f /conf/diskless_remount ]; then
184118611Snjl    nfspt=`/bin/cat /conf/diskless_remount`
185118611Snjl    if [ `expr "$nfspt" : '\(.\)'` = "/" ]; then
186118611Snjl	nfspt="${nfsroot}${nfspt}"
187118611Snjl    fi
188118611Snjl    mount_nfs $nfspt /conf
189118611Snjl    chkerr $? "mount_nfs $nfspt /conf"
190118611Snjl    to_umount="/conf"
191118611Snjlfi
192118611Snjl
193118611Snjl# Resolve templates in /conf/base, /conf/default, /conf/${bootp_ipbca},
194118611Snjl# and /conf/${bootp_ipa}.  For each subdirectory found within these
195118611Snjl# directories:
196118611Snjl#
197118611Snjl# - calculate memory filesystem sizes.  If the subdirectory (prior to
198118611Snjl#   NFS remounting) contains the file 'md_size', the contents specified
199118611Snjl#   in 512 byte sectors will be used to size the memory filesystem.  Otherwise
200118611Snjl#   8192 sectors (4MB) is used.
201118611Snjl#
202118611Snjl# - handle NFS remounts.  If the subdirectory contains the file
203118611Snjl#   diskless_remount, the contents of the file is NFS mounted over
204118611Snjl#   the directory.  For example /conf/base/etc/diskless_remount
205118611Snjl#   might contain 'myserver:/etc'.  NFS remounts allow you to avoid
206118611Snjl#   having to dup your system directories in /conf.  Your server must
207118611Snjl#   be sure to export those filesystems -alldirs, however.
208118611Snjl#   If the diskless_remount file contains a string beginning with a
209118611Snjl#   '/' it is assumed that the local nfsroot should be prepended to
210118611Snjl#   it before attemping to the remount.  This allows the root to be
211118611Snjl#   relocated without needing to change the remount files.
212118611Snjl#
213118611Snjlfor i in ${templates} ; do
214118611Snjl    for j in /conf/$i/* ; do
215118611Snjl	# memory filesystem size specification
216118611Snjl	#
217118611Snjl	subdir=${j##*/}
218118611Snjl	if [ -d $j -a -f $j/md_size ]; then
219118611Snjl	    eval md_size_$subdir=`cat $j/md_size`
220118611Snjl	fi
221118611Snjl
222118611Snjl	# remount
223118611Snjl	#
224118611Snjl	if [ -d $j -a -f $j/remount ]; then
225118611Snjl	    nfspt=`/bin/cat $j/remount`
226118611Snjl	    $nfspt $j
227118611Snjl	    chkerr $? "$nfspt $j"
228118611Snjl	    to_umount="${to_umount} $j" # XXX hope it is really a mount!
229118611Snjl	fi
230118611Snjl
231118611Snjl	# NFS remount
232118611Snjl	#
233118611Snjl	if [ -d $j -a -f $j/diskless_remount ]; then
234118611Snjl	    nfspt=`/bin/cat $j/diskless_remount`
235118611Snjl	    if [ `expr "$nfspt" : '\(.\)'` = "/" ]; then
236118611Snjl		nfspt="${nfsroot}${nfspt}"
237118611Snjl	    fi
238118611Snjl	    mount_nfs $nfspt $j
239118611Snjl	    chkerr $? "mount_nfs $nfspt $j"
240118611Snjl	    to_umount="${to_umount} $j"
241118611Snjl	fi
242118611Snjl    done
243118611Snjldone
244118611Snjl
245118611Snjl# - Create all required MFS filesystems and populate them from
246118611Snjl#   our templates.  Support both a direct template and a dir.cpio.gz
247118611Snjl#   archive.  Support dir.remove files containing a list of relative
248118611Snjl#   paths to remove.
249118611Snjl#
250118611Snjl# The dir.cpio.gz form is there to make the copy process more efficient,
251118611Snjl# so if the cpio archive is present, it prevents the files from dir/
252118611Snjl# from being copied.
253118611Snjl
254118611Snjlfor i in ${templates} ; do
255118611Snjl    for j in /conf/$i/* ; do
256118611Snjl	subdir=${j##*/}
257118611Snjl	if [ -d $j -a ! -f $j.cpio.gz  ]; then
258118611Snjl	    create_md $subdir
259118611Snjl	    cp -Rp $j/* /$subdir
260118611Snjl	fi
261118611Snjl    done
262118611Snjl    for j in /conf/$i/*.cpio.gz ; do
263118611Snjl	subdir=${j%*.cpio.gz}
264118611Snjl	subdir=${subdir##*/}
265118611Snjl	if [ -f $j ]; then
266118611Snjl	    create_md $subdir
267118611Snjl	    echo "Loading /$subdir from cpio archive $j"
268118611Snjl	    (cd / ; /stand/gzip -d < $j | /stand/cpio --extract -d )
269118611Snjl	fi
270118611Snjl    done
271118611Snjl    for j in /conf/$i/*.remove ; do
272118611Snjl	subdir=${j%*.remove}
273118611Snjl	subdir=${subdir##*/}
274118611Snjl	if [ -f $j ]; then
275118611Snjl	    # doubly sure it is a memory disk before rm -rf'ing
276118611Snjl	    create_md $subdir
277118611Snjl	    (cd /$subdir; rm -rf `/bin/cat $j`)
278118611Snjl	fi
279118611Snjl    done
280118611Snjldone
281118611Snjl
282118611Snjl# umount partitions used to fill the memory filesystems
283118611Snjl[ -n "${to_umount}" ] && umount $to_umount
284118611Snjl