工作中需要制作定制化的Ubuntu启动盘,之前有用 buildroot 做过,但是在安装工具方面还是没有Ubuntu灵活方便,所以还是准备基于Ubuntu来制作。
网上的文章大部分是基于Ubuntu 安装盘来制作,server版本约1GB大小,desktop版本约需要1.9GB。它有以下优点和缺点,即修改目标。
优点:
缺点:
首先研究 boot/grub/grub.cfg中的启动参数,其中 boot=casper 将影响 init脚本中的 BOOT参数值。
menuentry "Try Ubuntu without installing" {
set gfxpayload=keep
linux /casper/vmlinuz file=/cdrom/preseed/ubuntu.seed boot=casper quiet splash ---
initrd /casper/initrd.lz
然后研究 casper/initrd.lz,它负责加载squash文件系统。注意“. /scripts/${BOOT}”会提供“mountroot()”函数。
. /scripts/local
. /scripts/nfs
. /scripts/${BOOT}
parse_numeric ${ROOT}
maybe_break mountroot
mount_top
mount_premount
mountroot
以下是“mountroot()”函数及其注释,看得出来,大部分casper启动功能都在这里完成。
执行下面3个脚本,都可不予支持 |
run_scripts /scripts/casper-premount |
10driver_updates 只有带启动参数 debian-installer/driver-update=* 才会真正执行(去找启动盘上的 /cdrom/ubuntu-drivers/$(uname -r)下的驱动更新) |
|
20iso_scan 只有带启动参数 iso-scan/filename=* 才会真正执行 |
|
30custom_installation 只有带启动参数 debian-installer/custom-installation=* 才会真正执行 |
|
if [ ! -z "${NETBOOT}" ]; then | |
如果网络引导,可不予支持,不过支持这个功能调试比较方便 |
if do_netmount ; then |
livefs_root="${mountpoint}" | |
else | |
panic "Unable to find a live file system on the network" | |
fi | |
else | |
本地引导 | # Scan local devices for the image |
i=0 | |
尝试60次,每次间隔1秒 |
while [ "$i" -lt 60 ]; do |
》参考 find_livefs 的实现《 | livefs_root=$(find_livefs $i) |
搜索启动盘,优先搜启动参数live-media=*指定的设备, 如果没有指定或没能搜到,会在合适时机遍历所有设备搜索 |
if [ "${livefs_root}" ]; then |
其中 /bin/fstype 查看磁盘文件格式的功能不错,值得注意 | break |
fi | |
sleep 1 | |
i="$(($i + 1))" | |
done | |
fi | |
if [ -z "${livefs_root}" ]; then | |
panic "Unable to find a medium containing a live file system" | |
fi | |
如果启动参数指定 toram | if [ "${TORAM}" ]; then |
则将文件系统加载在内存 | live_dest="ram" |
如果启动参数指定 todisk=* | elif [ "${TODISK}" ]; then |
则将文件系统加载在磁盘 | live_dest="${TODISK}" |
fi | |
if [ "${live_dest}" ]; then | |
log_begin_msg "Copying live_media to ${live_dest}" | |
》参考 copy_live_to 的实现《 | copy_live_to "${livefs_root}" "${live_dest}" |
log_end_msg | |
fi | |
挂载镜像到目录,会调用setup_unionfs() 会从/cdrom/casper/下匹配 "ext2" "squashfs" "dir" 后缀的文件或文件夹,并挂载 |
mount_images_in_directory "${livefs_root}" "${rootmnt}" |
# initialize the /var/crash directory in overlayfs so that inotify for | |
crash处理机制,并不理解实际工作机制。 | # /var/crash works and update-notifier will notify of crashes |
touch /root/var/crash/crash.init | |
rm /root/var/crash/crash.init | |
log_end_msg | |
# Allow to override USERNAME and HOST based on media information | |
从initrd的/etc/casper.conf提取到FLAVOUR变量 | # make it skipable by setting FLAVOUR= in casper.conf |
如果FLAVOUR为空,从启动盘的/.disk/info提取 | if [ -f /cdrom/.disk/info ] && [ -z "$FLAVOUR" ]; then |
它将影响 HOST,USERNAME,USERFULLNAME,BUILD_SYSTEM | FLAVOUR="$(cut -d' ' -f1 "/cdrom/.disk/info" 2>/dev/null | tr '[A-Z]' '[a-z]')" || FLAVOUR= |
/etc/casper.conf里的值是 | if [ -n "$FLAVOUR" ]; then |
export USERNAME="ubuntu" | HOST=$FLAVOUR |
export USERFULLNAME="Live session user" | USERNAME=$FLAVOUR |
export HOST="ubuntu" | export HOST USERNAME |
export BUILD_SYSTEM="Ubuntu" | sed -i "s,USERNAME=.*,USERNAME=\"$FLAVOUR\",g; s,HOST=.*,HOST=\"$FLAVOUR\",g" /etc/casper.conf |
fi | |
fi | |
# Apply command lines override of HOST, USERNAME and USERFULLNAME | |
如果命令行有传参设置, 则优先使用其设置 HOST,USERNAME,USERFULLNAME |
[ -n "$CMD_HOST" ] && HOST=$CMD_HOST && export HOST |
[ -n "$CMD_USERNAME" ] && USERNAME=$CMD_USERNAME && export USERNAME | |
[ -n "$CMD_USERFULLNAME" ] && USERFULLNAME=$CMD_USERFULLNAME && export USERFULLNAME | |
if [ -n "$CMD_HOST" ] || [ -n "$CMD_USERNAME" ] || [ -n "$CMD_USERFULLNAME" ]; then | |
sed -i "s,USERNAME=.*,USERNAME=\"$USERNAME\",g; s,USERFULLNAME=.*,USERFULLNAME=\"$USERFULLNAME\",g; s,HOST=.*,HOST=\"$HOST\",g" /etc/casper.conf | |
fi | |
# unionfs-fuse needs /dev to be bind-mounted for the duration of | |
# casper-bottom; udev's init script will take care of things after that | |
如果是unionfs-fuse文件系统, | if [ "${UNIONFS}" = unionfs-fuse ]; then |
用“mount -n -o bind ...”挂载/dev设备目录 | mount -n -o bind /dev "${rootmnt}/dev" |
fi | |
# Open up two fifo's fd's for debconf-communicate to use. Speeds up | |
# the Casper process considerably. | |
log_begin_msg "Creating debconf-communicate fifo mechanism" | |
用 fifo 加速casper过程 | mkfifo /tmp/debconf-in.fifo |
结束前这些fifo会被释放删除 | mkfifo /tmp/debconf-out.fifo |
# Make the template database read-only, so that passthrough debconf | |
# instances can write to it directly; otherwise templates are only | |
# passed through when necessary. Use temporary config databases as | |
# well; we'll copy their contents back at the end. | |
DEBCONF_TMPDIR="$(chroot /root mktemp -dt debconf.XXXXXX)" | |
cp -a /root/var/cache/debconf/config.dat "/root$DEBCONF_TMPDIR/" | |
cp -a /root/var/cache/debconf/passwords.dat "/root$DEBCONF_TMPDIR/" | |
sed "s,^Filename: /var/cache/debconf/\(config\|passwords\).dat$,Filename: $DEBCONF_TMPDIR/\1.dat,; /^Name: templatedb/a\ | |
Readonly: true" /root/etc/debconf.conf >"/root$DEBCONF_TMPDIR/debconf.conf" | |
DEBCONF_SYSTEMRC="$DEBCONF_TMPDIR/debconf.conf" chroot /root debconf-communicate -fnoninteractive casper > /tmp/debconf-out.fifo < /tmp/debconf-in.fifo & | |
debconfpid="$!" | |
if [ ! -p /tmp/debconf-in.fifo ] || [ ! -p /tmp/debconf-out.fifo ]; then | |
log_warning_msg "failed to setup debconf-communicate channel" | |
fi | |
log_end_msg | |
# Order matters! | |
# These file descriptors must stay open until we're finished with | |
# debconf-communicate. | |
exec 4/tmp/debconf-in.fifo | |
maybe_break casper-bottom | |
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/casper-bottom" | |
》参考对应脚本《 | run_scripts /scripts/casper-bottom |
[ "$quiet" != "y" ] && log_end_msg | |
if [ "${UNIONFS}" = unionfs-fuse ]; then | |
umount "${rootmnt}/dev" | |
fi | |
# Close the fd's associated with debconf-communicate. | |
exec 3>&- 4<&- | |
rm -f /tmp/debconf-in.fifo | |
rm -f /tmp/debconf-out.fifo | |
wait $debconfpid | |
# Copy config database changes back to the master files. | |
chroot /root debconf-copydb tmpdb config \ | |
--config=Name:tmpdb --config=Driver:File \ | |
--config="Filename:$DEBCONF_TMPDIR/config.dat" | |
chroot /root debconf-copydb tmpdb passwords \ | |
--config=Name:tmpdb --config=Driver:File \ | |
--config="Filename:$DEBCONF_TMPDIR/passwords.dat" | |
rm -rf "$DEBCONF_TMPDIR" | |
exec 1>&6 6>&- | |
exec 2>&7 7>&- | |
kill "$tailpid" | |
cp casper.log "${rootmnt}/var/log/" | |
if [ -f /etc/casper.conf ]; then | |
cp /etc/casper.conf "${rootmnt}/etc/" | |
fi |
要修改 casper/initrd.lz, 先了解怎么解压和重新打包
# 解压 initrd.lz
mkdir initrd-dir
cd initrd-dir
cp ../initrd.lz initrd.lzma
unlzma -d initrd.lzma
cpio -id < initrd
rm initrd
# 重新打包为 initrd2.lz
cd initrd-dir
find . | cpio --quiet --dereference -o -H newc > ../initrd2
cd ..
lzma -7 initrd2
mv initrd2.lzma initrd2.lz
这样我们可以做一些改动,比如升级busybox。
/scripts/casper会解析启动参数 live-media-path,我们可以通过指定该值来改变 squashfs文件的搜索路径$LIVE_MEDIA_PATH。
live-media-path=*)
LIVE_MEDIA_PATH="${x#live-media-path=}"
export LIVE_MEDIA_PATH
echo "export LIVE_MEDIA_PATH=\"$LIVE_MEDIA_PATH\"" >> /etc/casper.conf ;;
下一节尝试 用cubic 工具定制启动盘,因为使用这个工具可以将注意力集中在配置软件上。