* Added support to build images with the serial line as the console
[bootcd.git] / build.sh
1 #!/bin/bash
2 #
3 # Builds custom BootCD ISO and USB images in the current
4 # directory. For backward compatibility, if an old-style static
5 # configuration is specified, that configuration file will be parsed
6 # instead of the current PLC configuration in
7 # /etc/planetlab/plc_config.
8 #
9 # Aaron Klingaman <alk@absarokasoft.com>
10 # Mark Huang <mlhuang@cs.princeton.edu>
11 # Copyright (C) 2004-2006 The Trustees of Princeton University
12 #
13 # $Id: build.sh,v 1.40 2006/07/25 23:51:39 mlhuang Exp $
14 #
15
16 PATH=/sbin:/bin:/usr/sbin:/usr/bin
17
18 CONFIGURATION=default
19 NODE_CONFIGURATION_FILE=
20 ALL=0
21
22 usage()
23 {
24     echo "Usage: build.sh [OPTION]..."
25     echo "      -c name         (Deprecated) Static configuration to use (default: $CONFIGURATION)"
26     echo "      -f planet.cnf   Node to customize CD for (default: none)"
27     echo "      -a              Build all images (default: only base images)"
28     echo "      -h              This message"
29     exit 1
30 }
31
32 # Get options
33 while getopts "c:f:ah" opt ; do
34     case $opt in
35         c)
36             CONFIGURATION=$OPTARG
37             ;;
38         f)
39             NODE_CONFIGURATION_FILE=$OPTARG
40             ;;
41         a)
42             ALL=1
43             ;;
44         h|*)
45             usage
46             ;;
47     esac
48 done
49
50 # Do not tolerate errors
51 set -e
52
53 # Change to our source directory
54 srcdir=$(cd $(dirname $0) && pwd -P)
55 pushd $srcdir
56
57 # Root of the isofs
58 isofs=$PWD/build/isofs
59
60 # Build reference image if it does not exist. This should only need to
61 # be executed once at build time, never at run time.
62 if [ ! -f $isofs/bootcd.img ] ; then
63     ./prep.sh
64 fi
65
66 # build/version.txt written by prep.sh
67 BOOTCD_VERSION=$(cat build/version.txt)
68
69 if [ -f /etc/planetlab/plc_config ] ; then
70     # Source PLC configuration
71     . /etc/planetlab/plc_config
72 fi
73
74 ### This support for backwards compatibility can be taken out in the
75 ### future. RC1 based MyPLCs set $PLC_BOOT_SSL_CRT in the plc_config
76 ### file, but >=RC2 based bootcd assumes that $PLC_BOOT_CA_SSL_CRT is
77 ### set.
78 if [ -z "$PLC_BOOT_CA_SSL_CRT" -a ! -z "$PLC_BOOT_SSL_CRT" ] ; then
79     PLC_BOOT_CA_SSL_CRT=$PLC_BOOT_SSL_CRT
80 fi
81
82 # If PLC configuration is not valid, try a static configuration
83 if [ -z "$PLC_BOOT_CA_SSL_CRT" -a -d configurations/$CONFIGURATION ] ; then
84     # (Deprecated) Source static configuration
85     . configurations/$CONFIGURATION/configuration
86     PLC_NAME="PlanetLab"
87     PLC_MAIL_SUPPORT_ADDRESS="support@planet-lab.org"
88     PLC_WWW_HOST="www.planet-lab.org"
89     PLC_WWW_PORT=80
90     if [ -n "$EXTRA_VERSION" ] ; then
91         BOOTCD_VERSION="$BOOTCD_VERSION $EXTRA_VERSION"
92     fi
93     PLC_BOOT_HOST=$PRIMARY_SERVER
94     PLC_BOOT_SSL_PORT=$PRIMARY_SERVER_PORT
95     PLC_BOOT_CA_SSL_CRT=configurations/$CONFIGURATION/$PRIMARY_SERVER_CERT
96     PLC_ROOT_GPG_KEY_PUB=configurations/$CONFIGURATION/$PRIMARY_SERVER_GPG
97 fi
98
99 FULL_VERSION_STRING="$PLC_NAME BootCD $BOOTCD_VERSION"
100
101 echo "* Building images for $FULL_VERSION_STRING"
102
103 # From within a myplc chroot /tmp is too small to build
104 # all possible images, whereas /data is part of the host
105 # filesystem and usually has sufficient space.  What we
106 # should do is check whether the expected amount of space
107 # is available.
108 [ -d /data ] && BUILDTMP=/data || BUILDTMP=/tmp
109
110 # Root of the ISO and USB images
111 echo "* Populating root filesystem..."
112 overlay=$(mktemp -d ${BUILDTMP}/overlay.XXXXXX)
113 install -d -m 755 $overlay
114 trap "rm -rf $overlay" ERR INT
115
116 # Create version files
117 echo "* Creating version files"
118
119 # Boot Manager compares pl_version in both places to make sure that
120 # the right CD is mounted. We used to boot from an initrd and mount
121 # the CD on /usr. Now we just run everything out of the initrd.
122 for file in $overlay/pl_version $overlay/usr/isolinux/pl_version ; do
123     mkdir -p $(dirname $file)
124     echo "$FULL_VERSION_STRING" >$file
125 done
126
127 # Install boot server configuration files
128 echo "* Installing boot server configuration files"
129
130 # We always intended to bring up and support backup boot servers,
131 # but never got around to it. Just install the same parameters for
132 # both for now.
133 for dir in $overlay/usr/boot $overlay/usr/boot/backup ; do
134         install -D -m 644 $PLC_BOOT_CA_SSL_CRT $dir/cacert.pem
135         install -D -m 644 $PLC_ROOT_GPG_KEY_PUB $dir/pubring.gpg
136         echo "$PLC_BOOT_HOST" >$dir/boot_server
137         echo "$PLC_BOOT_SSL_PORT" >$dir/boot_server_port
138         echo "/boot/" >$dir/boot_server_path
139 done
140
141 # (Deprecated) Install old-style boot server configuration files
142 install -D -m 644 $PLC_BOOT_CA_SSL_CRT $overlay/usr/bootme/cacert/$PLC_BOOT_HOST/cacert.pem
143 echo "$FULL_VERSION_STRING" >$overlay/usr/bootme/ID
144 echo "$PLC_BOOT_HOST" >$overlay/usr/bootme/BOOTSERVER
145 echo "$PLC_BOOT_HOST" >$overlay/usr/bootme/BOOTSERVER_IP
146 echo "$PLC_BOOT_SSL_PORT" >$overlay/usr/bootme/BOOTPORT
147
148 # Generate /etc/issue
149 echo "* Generating /etc/issue"
150
151 if [ "$PLC_WWW_PORT" = "443" ] ; then
152     PLC_WWW_URL="https://$PLC_WWW_HOST/"
153 elif [ "$PLC_WWW_PORT" != "80" ] ; then
154     PLC_WWW_URL="http://$PLC_WWW_HOST:$PLC_WWW_PORT/"
155 else
156     PLC_WWW_URL="http://$PLC_WWW_HOST/"
157 fi
158
159 mkdir -p $overlay/etc
160 cat >$overlay/etc/issue <<EOF
161 $FULL_VERSION_STRING
162 $PLC_NAME Node: \n
163 Kernel \r on an \m
164 $PLC_WWW_URL
165
166 This machine is a node in the $PLC_NAME distributed network.  It has
167 not fully booted yet. If you have cancelled the boot process at the
168 request of $PLC_NAME Support, please follow the instructions provided
169 to you. Otherwise, please contact $PLC_MAIL_SUPPORT_ADDRESS.
170
171 Console login at this point is restricted to root. Provide the root
172 password of the default $PLC_NAME Central administrator account at the
173 time that this CD was created.
174
175 EOF
176
177 # Set root password
178 echo "* Setting root password"
179
180 if [ -z "$ROOT_PASSWORD" ] ; then
181     # Generate an encrypted password with crypt() if not defined
182     # in a static configuration.
183     ROOT_PASSWORD=$(python <<EOF
184 import crypt, random, string
185 salt = [random.choice(string.letters + string.digits + "./") for i in range(0,8)]
186 print crypt.crypt('$PLC_ROOT_PASSWORD', '\$1\$' + "".join(salt) + '\$')
187 EOF
188 )
189 fi
190
191 # build/passwd copied out by prep.sh
192 sed -e "s@^root:[^:]*:\(.*\)@root:$ROOT_PASSWORD:\1@" build/passwd \
193     >$overlay/etc/passwd
194
195 # Install node configuration file (e.g., if node has no floppy disk or USB slot)
196 if [ -f "$NODE_CONFIGURATION_FILE" ] ; then
197     echo "* Installing node configuration file"
198     install -D -m 644 $NODE_CONFIGURATION_FILE $overlay/usr/boot/plnode.txt
199 fi
200
201 # Pack overlay files into a compressed archive
202 echo "* Compressing overlay image"
203 (cd $overlay && find . | cpio --quiet -c -o) | gzip -9 >$isofs/overlay.img
204
205 rm -rf $overlay
206 trap - ERR INT
207
208 # Calculate ramdisk size (total uncompressed size of both archives)
209 ramdisk_size=$(gzip -l $isofs/bootcd.img $isofs/overlay.img | tail -1 | awk '{ print $2; }') # bytes
210 ramdisk_size=$((($ramdisk_size + 1023) / 1024)) # kilobytes
211
212 # Write isolinux configuration
213 echo "$FULL_VERSION_STRING" >$isofs/pl_version
214 cat >$isofs/isolinux.cfg <<EOF
215 DEFAULT kernel
216 APPEND ramdisk_size=$ramdisk_size initrd=bootcd.img,overlay.img root=/dev/ram0 rw
217 DISPLAY pl_version
218 PROMPT 0
219 TIMEOUT 40
220 EOF
221
222 # Change back to output directory
223 popd
224
225 # Create ISO image
226 echo "* Creating ISO image"
227 iso="$PLC_NAME-BootCD-$BOOTCD_VERSION.iso"
228 mkisofs -o "$iso" \
229     -R -allow-leading-dots -J -r \
230     -b isolinux.bin -c boot.cat \
231     -no-emul-boot -boot-load-size 4 -boot-info-table \
232     $isofs
233
234 echo "* Creating ISO image with serial line support"
235 iso="$PLC_NAME-BootCD-$BOOTCD_VERSION-serial.iso"
236 cat >$isofs/isolinux.cfg <<EOF
237 SERIAL 0 115200
238 PROMPT 0
239 TIMEOUT 120
240 DISPLAY pl_version
241 DEFAULT serial
242 LABEL serial
243         KERNEL kernel
244         APPEND ramdisk_size=$ramdisk_size initrd=bootcd.img,overlay.img root=/dev/ram0 rw  console=ttyS0,115200n8
245 EOF
246 mkisofs -o "$iso" \
247     -R -allow-leading-dots -J -r \
248     -b isolinux.bin -c boot.cat \
249     -no-emul-boot -boot-load-size 4 -boot-info-table \
250     $isofs
251
252 # Create USB image
253 echo -n "* Creating USB image... "
254 usb="$PLC_NAME-BootCD-$BOOTCD_VERSION.usb"
255
256 # Leave 1 MB of free space on the VFAT filesystem
257 mkfs.vfat -C "$usb" $(($(du -sk $isofs | awk '{ print $1; }') + 1024))
258
259 # Mount it
260 tmp=$(mktemp -d ${BUILDTMP}/bootcd.XXXXXX)
261 mount -o loop "$usb" $tmp
262 trap "umount $tmp; rm -rf $tmp" ERR INT
263
264 # Populate it
265 echo -n " populating USB image... "
266 (cd $isofs && find . | cpio -p -d -u $tmp/)
267
268 # Use syslinux instead of isolinux to make the image bootable
269 rm -f $tmp/isolinux.cfg
270 cat >$tmp/syslinux.cfg <<EOF
271 DEFAULT kernel
272 APPEND ramdisk_size=$ramdisk_size initrd=bootcd.img,overlay.img root=/dev/ram0 rw
273 DISPLAY pl_version
274 PROMPT 0
275 TIMEOUT 40
276 EOF
277 umount $tmp
278 rmdir $tmp
279 trap - ERR INT
280
281 echo "making USB image bootable."
282 $srcdir/syslinux/unix/syslinux "$usb"
283
284
285 # Create USB image with serial line support
286 echo -n "* Creating USB image... "
287 usb="$PLC_NAME-BootCD-$BOOTCD_VERSION-serial.usb"
288
289 # Leave 1 MB of free space on the VFAT filesystem
290 mkfs.vfat -C "$usb" $(($(du -sk $isofs | awk '{ print $1; }') + 1024))
291
292 # Mount it
293 tmp=$(mktemp -d ${BUILDTMP}/bootcd.XXXXXX)
294 mount -o loop "$usb" $tmp
295 trap "umount $tmp; rm -rf $tmp" ERR INT
296
297 # Populate it
298 echo -n " populating USB image... "
299 (cd $isofs && find . | cpio -p -d -u $tmp/)
300
301 # Use syslinux instead of isolinux to make the image bootable
302 rm -f $tmp/isolinux.cfg
303 cat >$tmp/syslinux.cfg <<EOF
304 SERIAL 0 115200
305 PROMPT 0
306 TIMEOUT 120
307 DISPLAY pl_version
308 DEFAULT serial
309 LABEL serial
310         KERNEL kernel
311         APPEND ramdisk_size=$ramdisk_size initrd=bootcd.img,overlay.img root=/dev/ram0 rw  console=ttyS0,115200n8
312 EOF
313
314 umount $tmp
315 rmdir $tmp
316 trap - ERR INT
317
318 echo "making USB image with serial line support bootable."
319 $srcdir/syslinux/unix/syslinux "$usb"
320
321 [ $ALL -eq 0 ] && exit 0
322
323 # Setup CRAMFS related support
324 echo "* Setting up CRAMFS-based images"
325 tmp=$(mktemp -d ${BUILDTMP}/bootcd.XXXXXX)
326 cramfs=$(mktemp ${BUILDTMP}/cramfs.XXXXXX)
327 trap "$tmp; rm -rf $tmp $cramfs" ERR INT
328 pushd $tmp
329 gzip -d -c $isofs/bootcd.img  | cpio -diu
330 gzip -d -c $isofs/overlay.img | cpio -diu
331
332 # clean out unnecessary rpm lib
333 echo "* clearing var/lib/rpm/*"
334 rm -f var/lib/rpm/*
335
336 # bootcd requires this directory
337 mkdir -p mnt/confdevice
338
339 # relocate various directory to /tmp
340 rm -rf root
341 ln -fs /tmp/root root
342 ln -fs /sbin/init linuxrc 
343 ln -fs /tmp/resolv.conf etc/resolv.conf
344 ln -fs /tmp/etc/mtab etc/mtab
345
346 # have pl_rsysinit copy over appropriate etc & var directories into /tmp/etc/
347 # make /tmp/etc
348 echo "* renaming dirs in ./etc"
349 pushd etc
350 for dir in `find * -type d -prune | grep -v rc.d`; do mv ${dir} ${dir}_o; ln -fs /tmp/etc/${dir} ${dir} ; done
351 popd
352
353 echo "* renaming dirs in ./var"
354 # rename all top-level directories and put in a symlink to /tmp/var
355 pushd var
356 for dir in `find * -type d -prune`; do mv ${dir} ${dir}_o; ln -fs /tmp/var/${dir} ${dir} ; done
357 popd
358
359 #overwrite fstab to mount / as cramfs and /tmp as tmpfs
360 echo "* Overwriting etc/fstab to use cramfs and tmpfs"
361 rm -f ./etc/fstab
362 cat >./etc/fstab <<EOF
363 /dev/ram0     /              cramfs     ro              0 0
364 none          /dev/pts       devpts     gid=5,mode=620  0 0
365 none          /proc          proc       defaults        0 0
366 none          /sys           sysfs      defaults        0 0
367 EOF
368
369 pushd dev
370 rm -f console
371 mknod console c 5 1
372 #for i in 0 1 2 3 4 5 6 7 8; do rm -f ram${i} ; done
373 #for i in 0 1 2 3 4 5 6 7 8; do mknod ram${i} b 1 ${i} ; done
374 #ln -fs ram1 ram
375 #ln -fs ram0 ramdisk
376 popd
377
378 # update etc/inittab to start with pl_rsysinit
379 sed -i 's,pl_sysinit,pl_rsysinit,' etc/inittab
380
381 #calculate the size of /tmp based on the size of /etc & /var + 8MB slack
382 etcsize=$(du -s ./etc | awk '{ print $1 }')
383 varsize=$(du -s ./etc | awk '{ print $1 }')
384 let msize=($vsize+$esize+8192)/1024
385
386 # generate pl_rsysinit
387 cat > etc/rc.d/init.d/pl_rsysinit <<EOF
388 #!/bin/sh
389 # generated by build.sh
390 echo -n "pl_rsysinit: preparing /etc and /var for pl_sysinit..."
391 mount -t tmpfs -orw,size=${msize}M,mode=1777 tmpfs /tmp
392 mkdir -p /tmp/root
393 mkdir -p /tmp/etc
394 touch /tmp/etc/resolv.conf
395 touch /tmp/etc/mtab
396 mkdir -p /tmp/var
397
398 # make mtab happy
399 echo "tmpfs /tmp tmpfs rw,size=${msize}M,mode=1777 1 1" > /tmp/etc/mtab
400
401 # copy over directory contents of all _o directories from /etc and /var
402 # /tmp/etc and /tmp/var
403 pushd /etc
404 for odir in \$(cd /etc && ls -d *_o); do dir=\$(echo \$odir | sed 's,\_o$,,'); (mkdir -p /tmp/etc/\$dir && cd \$odir && find . | cpio -p -d -u /tmp/etc/\$dir); done
405 popd
406 pushd /var
407 for odir in \$(cd /var && ls -d *_o); do dir=\$(echo \$odir | sed 's,\_o$,,'); (mkdir -p /tmp/var/\$dir && cd \$odir && find . | cpio -p -d -u /tmp/var/\$dir); done
408 popd
409
410 echo "done"
411 # hand over to pl_sysinit
412 echo "pl_rsysinit: handing over to pl_sysinit"
413 /etc/init.d/pl_sysinit
414 EOF
415 chmod +x etc/rc.d/init.d/pl_rsysinit
416
417 popd
418
419 chown -R 0.0 $cramfs
420
421 #create the cramfs image
422 echo "* Creating cramfs image"
423 mkfs.cramfs $tmp/ $cramfs
424 # Leave 1 MB of free space on the VFAT filesystem
425 cramfs_size=$(($(du -sk $cramfs | awk '{ print $1; }')))
426 mv $cramfs ${BUILDTMP}/cramfs.img
427 rm -rf $tmp
428 trap - ERR INT
429
430 # Create ISO CRAMFS image
431 echo "* Creating ISO CRAMFS-based image"
432 iso="$PLC_NAME-BootCD-$BOOTCD_VERSION-cramfs.iso"
433
434 tmp=$(mktemp -d ${BUILDTMP}/bootcd.XXXXXX)
435 trap "$tmp; rm -rf $tmp" ERR INT
436 (cd $isofs && find . | grep -v "\.img$" | cpio -p -d -u $tmp/)
437 cat >$tmp/isolinux.cfg <<EOF
438 DEFAULT kernel
439 APPEND ramdisk_size=$cramfs_size initrd=cramfs.img root=/dev/ram0 ro
440 DISPLAY pl_version
441 PROMPT 0
442 TIMEOUT 40
443 EOF
444
445 cp ${BUILDTMP}/cramfs.img $tmp
446 mkisofs -o "$iso" \
447     -R -allow-leading-dots -J -r \
448     -b isolinux.bin -c boot.cat \
449     -no-emul-boot -boot-load-size 4 -boot-info-table \
450     $tmp
451
452 # Create ISO CRAMFS image with serial line support
453 echo "* Creating ISO image with cramfs and serial line support"
454 cat >$tmp/isolinux.cfg <<EOF
455 SERIAL 0 115200
456 PROMPT 0
457 TIMEOUT 120
458 DISPLAY pl_version
459 DEFAULT serial
460 LABEL serial
461         KERNEL kernel
462         APPEND ramdisk_size=$cramfs_size initrd=cramfs.img root=/dev/ram0 ro  console=ttyS0,115200n8
463 EOF
464
465 iso="$PLC_NAME-BootCD-$BOOTCD_VERSION-cramfs-serial.iso"
466 mkisofs -o "$iso" \
467     -R -allow-leading-dots -J -r \
468     -b isolinux.bin -c boot.cat \
469     -no-emul-boot -boot-load-size 4 -boot-info-table \
470     $tmp
471
472 rm -rf $tmp
473 trap - ERR INT
474
475 # Create USB CRAMFS based image
476 echo "* Creating USB CRAMFS based image"
477 usb="$PLC_NAME-BootCD-$BOOTCD_VERSION-cramfs.usb"
478
479 # leave 1MB of space on the USB VFAT
480 let vfat_size=${cramfs_size}+2048
481
482 # Make VFAT filesystem for USB
483 mkfs.vfat -C "$usb" $vfat_size
484
485 # Mount it
486 tmp=$(mktemp -d ${BUILDTMP}/bootcd.XXXXXX)
487 mount -o loop "$usb" $tmp
488 trap "umount $tmp; rm -rf $tmp ${BUILDTMP}/cramfs.img" ERR INT
489
490 # Populate it
491 echo "* Populating USB with overlay images and cramfs"
492 (cd $isofs && find . | grep -v "\.img$" | cpio -p -d -u $tmp/)
493 cp ${BUILDTMP}/cramfs.img $tmp/
494
495 # Use syslinux instead of isolinux to make the image bootable
496 cat >$tmp/syslinux.cfg <<EOF
497 TIMEOUT 120
498 DISPLAY pl_version
499 DEFAULT vga
500 LABEL vga
501         KERNEL kernel
502         APPEND ramdisk_size=$cramfs_size initrd=cramfs.img root=/dev/ram0 ro
503 EOF
504 umount $tmp
505 rmdir $tmp
506 trap - ERR INT
507
508 echo "* Making USB CRAMFS based image bootable"
509 $srcdir/syslinux/unix/syslinux "$usb"
510
511 # Create USB CRAMFS based image w/ serial line support
512 echo "* Creating USB CRAMFS based image w/ serial line support"
513 usb="$PLC_NAME-BootCD-$BOOTCD_VERSION-cramfs-serial.usb"
514
515 # leave 4MB of space on the USB VFAT
516 let vfat_size=${cramfs_size}+2048
517
518 # Make VFAT filesystem for USB
519 mkfs.vfat -C "$usb" $vfat_size
520
521 # Mount it
522 tmp=$(mktemp -d ${BUILDTMP}/bootcd.XXXXXX)
523 mount -o loop "$usb" $tmp
524 trap "umount $tmp; rm -rf $tmp ${BUILDTMP}/cramfs.img" ERR INT
525
526 # Populate it
527 echo "* Populating USB with overlay images and cramfs"
528 (cd $isofs && find . | grep -v "\.img$" | cpio -p -d -u $tmp/)
529 cp ${BUILDTMP}/cramfs.img $tmp/
530
531 # Use syslinux instead of isolinux to make the image bootable
532 cat >$tmp/syslinux.cfg <<EOF
533 SERIAL 0 115200
534 PROMPT 0
535 TIMEOUT 120
536 DISPLAY pl_version
537 DEFAULT serial
538 LABEL serial
539         KERNEL kernel
540         APPEND ramdisk_size=$cramfs_size initrd=cramfs.img root=/dev/ram0 ro  console=ttyS0,115200n8
541 EOF
542 umount $tmp
543 rmdir $tmp
544 trap - ERR INT
545
546 echo "* Making USB CRAMFS based image /w serial line support bootable"
547 $srcdir/syslinux/unix/syslinux "$usb"
548
549 exit 0