summaryrefslogtreecommitdiffstats
path: root/etc/rc.d/initdiskless
blob: 23d7d569271565e8015912f4827d1349545eb4c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#!/bin/sh
#
# Copyright (c) 1999  Matt Dillon
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
# REQUIRE: preseedrandom
# PROVIDE: initdiskless
# KEYWORD: nojail
# BEFORE: ipfw


# On entry to this script the entire system consists of a read-only root
# mounted via NFS. The kernel has run BOOTP and configured an interface
# (otherwise it would not have been able to mount the NFS root!)
#
# We use the contents of /conf to create and populate memory filesystems
# that are mounted on top of this root to implement the writable
# (and host-specific) parts of the root filesystem, and other volatile
# filesystems.
#
# The hierarchy in /conf has the form /conf/T/M/ where M are directories
# for which memory filesystems will be created and filled,
# and T is one of the "template" directories below:
#
#  base		universal base, typically a replica of the original root;
#  default	secondary universal base, typically overriding some
#		of the files in the original root;
#  ${ipba}	where ${ipba} is the assigned broadcast IP address
#  ${class}	where ${class} is a list of directories supplied by
#		bootp/dhcp through the T134 option.
#		${ipba} and ${class} are typicall used to configure features
#		for group of diskless clients, or even individual features;
#  ${ip}	where ${ip} is the machine's assigned IP address, typically
#		used to set host-specific features;
#
# Template directories are scanned in the order they are listed above,
# with each sucessive directory overriding (merged into) the previous one;
# non-existing directories are ignored.
#
# The existence of a directory /conf/T/M causes this script to create a
# memory filesystem mounted as /M on the client.
#
# Some files in /conf have special meaning, namely:
#
# Filename	Action
# ----------------------------------------------------------------
# /conf/T/M/remount
#		The contents of the file is a mount command. E.g. if
# 		/conf/1.2.3.4/foo/remount contains "mount -o ro /dev/ad0s3",
#		then /dev/ad0s3 will be be mounted on /conf/1.2.3.4/foo/
#
# /conf/T/M/diskless_remount
#		The contents of the file points to an NFS filesystem. E.g. if
#		/conf/base/etc/diskless_remount contains "foo.com:/etc",
#		then foo.com:/etc will be be mounted on /conf/base/etc/
#		If the file contains a pathname starting with "/", then
#		the root path is prepended to it; this allows relocation of
#		the root filesystem withouth changing configuration files.
#
# /conf/T/M/md_size
#		The contents of the file specifies the size of the memory
#		filesystem to be created, in 512 byte blocks.
#		The default size is 10240 blocks (5MB). E.g. if
#		/conf/base/etc/md_size contains "30000" then a 15MB MFS
#		will be created. In case of multiple entries for the same
#		directory M, the last one in the scanning order is used.
#		NOTE: If you only need to create a memory filesystem but not
#		initialize it from a template, it is preferrable to specify
#		it in fstab e.g. as  "md /tmp mfs -s=30m,rw 0 0"
#
# /conf/T/SUBDIR.cpio.gz
#		The file is cpio'd into /SUBDIR (and a memory filesystem is
#		created for /SUBDIR if necessary). The presence of this file
#		prevents the copy from /conf/T/SUBDIR/
#
# /conf/T/SUBDIR.remove
#		The list of paths contained in the file are rm -rf'd
#		relative to /SUBDIR.
#
# You will almost universally want to create the following files under /conf
#
# File				Content
# ----------------------------	------------------------------------------
# /conf/base/etc/md_size	size of /etc filesystem
# /conf/base/diskless_remount	"/etc"
# /conf/default/etc/rc.conf	generic diskless config parameters
# /conf/default/etc/fstab	generic diskless fstab e.g. like this
#
#	foo:/root_part		/	nfs	ro		0 0
#	foo:/usr_part		/usr	nfs     ro		0 0
#	foo:/home_part		/home   nfs     rw      	0 0
#	md			/tmp	mfs     -s=30m,rw	0 0
#	md			/var	mfs	-s=30m,rw	0 0
#	proc			/proc	procfs	rw		0 0
#
# plus, possibly, overrides for password files etc.
#
# NOTE!  /var, /tmp, and /dev will be typically created elsewhere, e.g.
# as entries in the fstab as above.
# Those filesystems should not be specified in /conf.
#
# (end of documentation, now get to the real code)

dlv=`/sbin/sysctl -n vfs.nfs.diskless_valid 2> /dev/null`
[ ${dlv:=0} -eq 0 ] && [ ! -f /etc/diskless ] && exit 0

# chkerr:
#
# Routine to check for error
#
#	checks error code and drops into shell on failure.
#	if shell exits, terminates script as well as /etc/rc.
#
chkerr() {
    case $1 in
    0)
	;;
    *)
	echo "$2 failed: dropping into /bin/sh"
	/bin/sh
	# RESUME
	;;
    esac
}

# Create a generic memory disk
#
mount_md() {
    /sbin/mdmfs -i 4096 -s $1 -M md $2
}

# Create the memory filesystem if it has not already been created
#
create_md() {
    if [ "x`eval echo \\$md_created_$1`" = "x" ]; then
	if [ "x`eval echo \\$md_size_$1`" = "x" ]; then
	    md_size=10240
	else
	    md_size=`eval echo \\$md_size_$1`
	fi
	mount_md $md_size /$1
	/bin/chmod 755 /$1
	eval md_created_$1=created
    fi
}

# DEBUGGING
#
# set -v

# Figure out our interface and IP.
#
bootp_ifc=""
bootp_ipa=""
bootp_ipbca=""
if [ ${dlv:=0} -ne 0 ] ; then
	iflist=`ifconfig -l`
	for i in ${iflist} ; do
	    set -- `ifconfig ${i}`
	    while [ $# -ge 1 ] ; do
		if [ "${bootp_ifc}" = "" -a "$1" = "inet" ] ; then
		    bootp_ifc=${i} ; bootp_ipa=${2} ; shift
		fi
		if [ "${bootp_ipbca}" = "" -a "$1" = "broadcast" ] ; then
		    bootp_ipbca=$2; shift
		fi
		shift
	    done
	    if [ "${bootp_ifc}" != "" ] ; then
		break
	    fi
	done
	# Insert the directories passed with the T134 bootp cookie
	# in the list of paths used for templates.
	i="`/sbin/sysctl -n kern.bootp_cookie`"
	[ "${i}" != "" ] && bootp_ipbca="${bootp_ipbca} ${i}"

	echo "Interface ${bootp_ifc} IP-Address ${bootp_ipa} Broadcast ${bootp_ipbca}"
fi

# Figure out our NFS root path
#
set -- `mount -t nfs`
while [ $# -ge 1 ] ; do
    if [ "$2" = "on" -a "$3" = "/" ]; then
	nfsroot="$1"
	break
    fi
    shift
done

# The list of directories with template files
templates="base default ${bootp_ipbca} ${bootp_ipa}"

# The list of filesystems to umount after the copy
to_umount=""

# If /conf/diskless_remount exists, remount all of /conf.  This allows
# multiple roots to share the same conf files.
if [ -d /conf -a -f /conf/diskless_remount ]; then
    nfspt=`/bin/cat /conf/diskless_remount`
    if [ `expr "$nfspt" : '\(.\)'` = "/" ]; then
	nfspt="${nfsroot}${nfspt}"
    fi
    mount_nfs $nfspt /conf
    chkerr $? "mount_nfs $nfspt /conf"
    to_umount="/conf"
fi

# Resolve templates in /conf/base, /conf/default, /conf/${bootp_ipbca},
# and /conf/${bootp_ipa}.  For each subdirectory found within these
# directories:
#
# - calculate memory filesystem sizes.  If the subdirectory (prior to
#   NFS remounting) contains the file 'md_size', the contents specified
#   in 512 byte sectors will be used to size the memory filesystem.  Otherwise
#   8192 sectors (4MB) is used.
#
# - handle NFS remounts.  If the subdirectory contains the file
#   diskless_remount, the contents of the file is NFS mounted over
#   the directory.  For example /conf/base/etc/diskless_remount
#   might contain 'myserver:/etc'.  NFS remounts allow you to avoid
#   having to dup your system directories in /conf.  Your server must
#   be sure to export those filesystems -alldirs, however.
#   If the diskless_remount file contains a string beginning with a
#   '/' it is assumed that the local nfsroot should be prepended to
#   it before attemping to the remount.  This allows the root to be
#   relocated without needing to change the remount files.
#
for i in ${templates} ; do
    for j in /conf/$i/* ; do
	# memory filesystem size specification
	#
	subdir=${j##*/}
	if [ -d $j -a -f $j/md_size ]; then
	    eval md_size_$subdir=`cat $j/md_size`
	fi

	# remount
	#
	if [ -d $j -a -f $j/remount ]; then
	    nfspt=`/bin/cat $j/remount`
	    $nfspt $j
	    chkerr $? "$nfspt $j"
	    to_umount="${to_umount} $j" # XXX hope it is really a mount!
	fi

	# NFS remount
	#
	if [ -d $j -a -f $j/diskless_remount ]; then
	    nfspt=`/bin/cat $j/diskless_remount`
	    if [ `expr "$nfspt" : '\(.\)'` = "/" ]; then
		nfspt="${nfsroot}${nfspt}"
	    fi
	    mount_nfs $nfspt $j
	    chkerr $? "mount_nfs $nfspt $j"
	    to_umount="${to_umount} $j"
	fi
    done
done

# - Create all required MFS filesystems and populate them from
#   our templates.  Support both a direct template and a dir.cpio.gz
#   archive.  Support dir.remove files containing a list of relative
#   paths to remove.
#
# The dir.cpio.gz form is there to make the copy process more efficient,
# so if the cpio archive is present, it prevents the files from dir/
# from being copied.

for i in ${templates} ; do
    for j in /conf/$i/* ; do
	subdir=${j##*/}
	if [ -d $j -a ! -f $j.cpio.gz  ]; then
	    create_md $subdir
	    cp -Rp $j/* /$subdir
	fi
    done
    for j in /conf/$i/*.cpio.gz ; do
	subdir=${j%*.cpio.gz}
	subdir=${subdir##*/}
	if [ -f $j ]; then
	    create_md $subdir
	    echo "Loading /$subdir from cpio archive $j"
	    (cd / ; /stand/gzip -d < $j | /stand/cpio --extract -d )
	fi
    done
    for j in /conf/$i/*.remove ; do
	subdir=${j%*.remove}
	subdir=${subdir##*/}
	if [ -f $j ]; then
	    # doubly sure it is a memory disk before rm -rf'ing
	    create_md $subdir
	    (cd /$subdir; rm -rf `/bin/cat $j`)
	fi
    done
done

# umount partitions used to fill the memory filesystems
[ -n "${to_umount}" ] && umount $to_umount
OpenPOWER on IntegriCloud