Helio/net-imager
From FBSD_tips
Please read the parent article before continuing.
WARNING: THIS ARTICLE IS A DRAFT. STEPS MAY BE INCORRECT OR DESTRUCTIVE.
WARNING: THESE METHODS ARE DESTRUCTIVE. BACKUP ALL DATA BEFORE YOU START, OR IT WILL BE LOST
Contents |
[edit] Programs
To create this product, you'll need to following binaries (and their libs) from /usr/bin or /usr/sbin:
- grep
- awk
- head
- cut
- uniq
You'll also need the following ports, with these binaries installed:
- mbone/udpcast
- udp-sender
- udp-receiver
[edit] Setting some sane defaults
This starts off with the same /etc/rc as we created previously, however, we first create some useful environment variables to use as defaults:
export RATE=90m
export PRODUCT="$(kenv -q smbios.planar.product | awk '{ print $0; }')"
export SERIAL="$(kenv -q smbios.system.serial | awk '{ print $0; }')"
export PRODUCTNAME="$(kenv -q smbios.system.maker) $(kenv -q smbios.system.product)"
export PRODUCTNAME=$(echo ${PRODUCTNAME} | awk '{ print $0; }')
This sets our default packet rate to 90 megabit (a quirk in the multicast udp implementation we have, requires it), as well as setting some variables about the machine, for possible use later.
Now, we'll add a routine that gives us all the device nodes of class 'disk' that we expect to use:
get_disk () {
echo $(geom disk status -s | awk '{ print $1; }' | grep -v afd | grep -v cd | grep -v acd | grep -v fd)
}
and set DISK to the first result:
export DISK=$(get_disk | awk ' { print $1; }')
Let's add a routine, like get_disk, for NIC's. Note we filter out the loopback, and various point to point virtual devices. Odds are you'd never use them in an imaging situation, and they tend to end up high on the list (so the default could be slip0, which is unusable with just a dhclient. If you need these methods, it's up to you to add the glue to configure them:
list_if () {
for i in `ifconfig -l`
do
case ${i} in
lo*)
;;
plip*)
;;
slip*)
;;
fwe*)
;;
ppp*)
;;
*)
echo "${i}"
;;
esac
done
}
Now, let's first set IF to the interface used to PXE boot, and if it does not exist, set it to the first result from list_if.
IF=`kenv -q boot.netif.name`
if [ "${IF}" = "" ] ; then
IF=$(list_if | head -n 1)
fi
export IF
Then we use dhclient to configure it:
dhclient ${IF}
[edit] Presentation
Every good product needs to have a halfway good presentation, and it does not involve bells and whistles. Merely making the information available in a sane way. These two routines do just that:
This one shows a 'pretty print' name of a NIC, which can be more useful for a user then 'fxp0':
pretty_if () {
echo "($(dmesg | grep ${1} | grep device | cut -d "<" -f 2 | cut -d ">" -f 1 | uniq))"
}
This routine prints a rather verbose header (the empty echo lines are to emulate 'clear', so that this script works on extremely dumb terminals). Some of the information provided to you is the machines name, manufacturer, and serial number (for administrative purposes), the NIC it will use, and the disk it will use. There is room for improvement here, for example, returning the MAC for the NIC and the size and model of the disk drive.
print_header () {
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo ""
echo "BSD Imager ${VERSION}"
echo "Model: ${PRODUCT} \"${PRODUCTNAME}\""
echo "Serial: ${SERIAL}"
echo "NIC: ${IF} $(pretty_if ${IF})"
echo "Target Disk: ${DISK}"
echo "Current Rate Limit: ${RATE}"
echo "------------------------------------"
}
[edit] A helpful little automater
Right now we'll put in a function called receive-image, that implements a basic image receiving routine; It will be used for 'unattended' installations:
receive-image() {
udp-receiver --interface ${IF} -f /dev/${DISK}
}
[edit] The big routine
First, we check to see if 'unattended' is set in the kernel's environment.
if [ "$(kenv -q unattended)" = "" ] ; then
This allows us to implement a boot menu option for 'unattended' mode, or have it as a static option in loader.conf.
Now, we create an infinite loop to wrap around the main menu:
until [ "${PATH}" = "1" ]
do
We print our pretty little header:
print_header
And supply all of the options available:
echo " 1 - Send image"
echo " 2 - Recieve image"
echo " 3 - Change Target Disk"
echo " 4 - Change Rate Limit (requires some tweaking)"
echo " 5 - Change NIC"
echo " 6 - Install FreeBSD"
echo " z - Wipe Disk"
echo " s - Drop to Shell"
echo " q - Reboot"
Now we get an option using read:
echo ""
echo ""
read -p "Enter choice: " choice
We use choice to determine our action via a generic 'case' statement:
case "${choice}" in
These are the two simplest options, q reboots, and s forks tcsh for commands (I find being able to fork tcsh to be a requirement for most products, as they allow you to test in the environment directly)
q)
reboot ;;
s)
tcsh ;;
Now, the actual sending and receiving of disk images. udp-sender is the multicast UDP program we'll use, as it's intended for use in these situations (they even have a CD image on their website which implements some of the features we are creating, except Linux oriented instead of FreeBSD oriented):
1)
udp-sender -f /dev/${DISK} --max-bitrate ${RATE} --interface ${IF}
;;
2)
We'll use the receive-image function we created earlier, so everything is in one place:
receive-image
;;
The next block is for changing options, which for our products is just changing the disk, nic, and the rate cap.
3)
echo "Avaliable Disk's:"
This will give us a nice list of available disk devices the user can choose from, using the same routine we used to get the default disk:
get_disk
read -p "Enter New Target Disk [${DISK}]: " NDISK
Check to make sure it isn't empty (note, we could check to see if the disk device is in the list, however, that is left as an exercise for the user):
if [ "${NDISK}" != "" ] ; then
DISK=${NDISK}
fi
Make it an environment variable:
export DISK
;;
We'll do the same for RATE, except we don't need to give a list, just examples:
4)
read -p "Enter New Limit (ex. 90m, 56k, 1000m) [${RATE}]: " NRATE
if [ "${NRATE}" != "" ] ; then
RATE=${NRATE}
fi
export RATE
;;
Now, for IF, we go through the list returned by list_if, and use the pretty_if routine we described earlier, to show the user the logical name for an interface.
5)
echo "Available NIC's:"
LIST=$(list_if)
for i in ${LIST}
do
echo "${i} $(pretty_if ${i})"
done
read -p "Enter NIC (ex. xl0, fxp0) [${IF}]: " NIF
if [ "${NIF}" != "" ] ; then
IF=${NIF}
fi
export IF
;;
And we'll add a simple and fast disk eraser system for creating 'clean' images.
z)
echo "Wiping Disk, May take hours.."
dd if=/dev/zero bs=16m of=/dev/${DISK}
;;
And a wrapper for the freebsd_install command:
6)
freebsd_install
;;
We'll fall back to doing nothing, as any default is potentially destructive:
*) ;;
esac
Simple pause to view output.
echo ""
echo ""
echo ""
echo "Press Enter to Continue"
read BLAH
done
Now, for what we do if 'unattended' is set:
else
print_header
receive-image
fi
And to keep init from throwing errors from our lack of an /etc/ttys file:
reboot
[edit] A Naive FreeBSD Cloning Script
freebsd_install () {
until [ "${PATH}" = "1" ]
do
print_header
echo " 1 - Send image"
echo " 2 - Recieve image"
echo " 3 - Change Target Disk"
echo " 4 - Change Rate Limit (requires some tweaking)"
echo " 5 - Change NIC"
echo " q - Back to main"
echo ""
echo ""
read -p "Enter choice: " choice
case "${choice}" in
1)
mount /dev/${DISK}s1a /mnt
cd /mnt
tar -cpvf - * | udp-sender --mcast-all-addr ${ADDRESS} --max-bitrate ${RATE} --interface ${IF}
cd ..
umount /mnt
;;
2)
fdisk -IB /dev/${DISK}
bsdlabel -wB /dev/${DISK}s1
LABEL=$(dd if=/dev/urandom bs=16m count=2 | md5 | cut -b 1-31)
newfs -L ${LABEL} /dev/${DISK}s1a
mount /dev/${DISK}s1a /mnt
udp-receiver --mcast-all-addr ${ADDRESS} --interface ${IF} | tar -xpf - -C /mnt
dd if=/dev/zero of=/mnt/swapfile bs=16m count=32
echo "/dev/ufs/${LABEL} / ufs rw 1 1" >/mnt/etc/fstab
echo "swapfile=\"/swapfile\"" >>/mnt/etc/rc.conf
echo "geom_label_load=\"YES\"" >> /mnt/boot/loader.conf
grep -v hostname /mnt/etc/rc.conf /mnt/etc/rc.conf.new
mv /mnt/etc/rc.conf.new /mnt/etc/rc.conf
echo "hostname=\"\$\(kenv -q system.planer.product\)\"" >> /mnt/etc/rc.conf
cp /mnt/boot/boot /var/
cp /mnt/boot/mbr /var/
umount /dev/${DISK}s1a
bsdlabel -B -b /var/boot /dev/${DISK}s1
fdisk -B -b /var/mbr /dev/${DISK}
;;
q)
return ;;
3)
echo "Avaliable Disk's:"
get_disk
ODISK=${DISK}
read -p "Enter New Target Disk [${ODISK}]: " DISK
if [ "${DISK}" = "" ] ; then
DISK=${ODISK}
fi
;;
4)
ORATE=${RATE}
read -p "Enter New Limit (ex. 90m, 56k, 1000m) [${ORATE}]: " RATE
if [ "${RATE}" = "" ] ; then
RATE=${ORATE}
fi
;;
5)
OIF=${IF}
echo "Available NIC's:"
LIST=list_if
for i in ${LIST}
do
echo "${i} $(pretty_if ${i})"
done
read -p "Enter NIC (ex. xl0, fxp0) [${OIF}]: " IF
if [ "${IF}" = "" ] ; then
IF=${OIF}
fi
;;
i)
OADDRESS=${ADDRESS}
read -p "Enter New Image Group (ex. 224.0.0.1, 224.16.2.3) [${OADDRESS}]: " ADDRESS
if [ "${ADDRESS}" = "" ] ; then
ADDRESS=${OADDRESS}
fi
;;
*) ;;
esac
echo ""
echo ""
echo ""
echo "Press Enter to Continue"
read BLAH
done
}
[edit] Download
This is actually a listing of an already existing system, which can be found [[1]]
Note I do not support it, as it has some bugs, and is as of right now, unmaintained. At some point I will maintain it again, however, it is mostly functional.
