blob: 24210b9510726ee319ca703a5fdae34f1443ce18 [file] [log] [blame]
#!/bin/bash
#
# Copyright 2021 The Cobalt Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script downloads and customizes a Raspbian image for running
# Cobalt. Qemu is used for running on-target customization steps.
# Please see README for full description
# Image versions to use
BASEDIR=raspbian_lite-2020-02-14
BASENAME=2020-02-13-raspbian-buster-lite
# Image and script sources
IMAGE_URL=http://downloads.raspberrypi.org/raspbian_lite/images
SHRINK_SCRIPT="https://raw.githubusercontent.com/aoakley/cotswoldjam/\
master/raspbian-shrink/raspbian-shrink"
# Set up locations for temporary files
STAGING_DIR=/tmp/pi2_image
IMG_FILE=${STAGING_DIR}/${BASENAME}.img
SHRUNK_IMG_FILE=${STAGING_DIR}/${BASENAME}_shrunk_$(date +"%Y%m%d").img
BOOT_MOUNT=${STAGING_DIR}/boot
ROOT_MOUNT=${STAGING_DIR}/rootfs
SSH_KEY_FILE=${STAGING_DIR}/pi_key
QEMU_ADDRESS=127.0.0.1
# Reuse some command args
RUN_ON_PI="ssh -i ${SSH_KEY_FILE} -o StrictHostKeyChecking=no -p 8022 pi@${QEMU_ADDRESS} sudo"
WGET="wget --show-progress -nc"
# Check expected tools are available
function tools_check() {
if ! command -v qemu-system-arm --version &> /dev/null ; then
echo Could not find QEMU, see README for instructions
return
fi
if ! command -v dcfldd --version &> /dev/null ; then
echo Could not find dcfldd, see README for instructions
fi
}
function fetch_image() {
mkdir -p ${STAGING_DIR}
${WGET} ${IMAGE_URL}/${BASEDIR}/${BASENAME}.zip \
-O ${STAGING_DIR}/${BASENAME}.zip
if [[ ! -f ${IMG_FILE} ]]; then
unzip ${STAGING_DIR}/${BASENAME}.zip -d ${STAGING_DIR}
fi
}
# Mount the image file boot partition
function mount_boot() {
BOOT_LOCATION=$(fdisk -l ${IMG_FILE}| egrep img1| tr -s ' '| cut -f 2 -d " ")
MOUNT_OFFSET=$(bc <<< "$BOOT_LOCATION * 512")
mkdir -p ${BOOT_MOUNT}
sudo mount -v -o offset=${MOUNT_OFFSET} ${IMG_FILE} ${BOOT_MOUNT}
}
# Mount the image file root partition
function mount_root() {
BOOT_LOCATION=$(fdisk -l ${IMG_FILE}| egrep img2| tr -s ' '| cut -f 2 -d " ")
MOUNT_OFFSET=$(bc <<< "$BOOT_LOCATION * 512")
mkdir -p ${ROOT_MOUNT}
sudo mount -v -o offset=${MOUNT_OFFSET} -t ext4 ${IMG_FILE} ${ROOT_MOUNT}
}
function unmount_root() {
sudo umount ${ROOT_MOUNT}
}
function unmount_boot() {
sudo umount ${BOOT_MOUNT}
}
function modify_boot() {
# Set GPU/main memory split
sudo sh -c "echo gpu_mem=512 >> ${BOOT_MOUNT}/config.txt"
# Ensure 1st boot image resize runs
sudo touch ${BOOT_MOUNT}/1stboot.txt
# Drop auto-resize script in place, crontab entry will be added later
sudo cp -v auto-resize-partition.sh ${BOOT_MOUNT}/
sudo cp -v crontab.txt ${BOOT_MOUNT}/
sudo chmod +x ${BOOT_MOUNT}/auto-resize-partition.sh
}
function modify_root() {
# Enable SSH by default
sudo rm ${ROOT_MOUNT}/etc/{rc2.d,rc3.d,rc4.d,rc5.d}/K01ssh
for i in 2 3 4 5 ; do
sudo ln -s ../init.d/ssh ${ROOT_MOUNT}/etc/rc${i}.d/S02ssh
done
sudo ln -s /lib/systemd/system/ssh.service \
${ROOT_MOUNT}/etc/systemd/system/multi-user.target.wants/ssh.service
sudo ln -s /lib/systemd/system/ssh.service \
${ROOT_MOUNT}/etc/systemd/system/sshd.service
}
function get_kernel() {
# Get matching kernel and DTB files from boot partition
cp ${BOOT_MOUNT}/kernel7.img ${STAGING_DIR}
cp ${BOOT_MOUNT}/bcm2709-rpi-2-b.dtb ${STAGING_DIR}
}
function run_qemu() {
tools_check
# Ensure image is in usable size for Qemu
qemu-img resize ${IMG_FILE} 4G
# Boot qemu in background
qemu-system-arm $@ \
-machine raspi2 \
-drive file=${IMG_FILE},format=raw,if=sd \
-dtb ${STAGING_DIR}/bcm2709-rpi-2-b.dtb \
-kernel ${STAGING_DIR}/kernel7.img \
-append 'rw earlycon=pl011,0x3f201000 console=ttyAMA0 \
loglevel=8 root=/dev/mmcblk0p2 fsck.repair=yes \
net.ifnames=0 rootwait memtest=1 \
dwc_otg.fiq_fsm_enable=0' \
-m 1G -smp 4 \
-no-reboot \
-netdev user,id=net0,hostfwd=tcp::8022-:22 \
-usb -device usb-kbd -device usb-tablet \
-device usb-net,netdev=net0
}
function run_qemu_backgrounded() {
run_qemu -daemonize
}
# Useful for local image testing
function run_qemu_console() {
run_qemu -serial stdio
}
function kill_qemu() {
pkill -9 qemu-system-arm
}
function make_ssh_key() {
if [[ ! -f ${SSH_KEY_FILE} ]]; then
ssh-keygen -q -N "" -b 1024 -f ${SSH_KEY_FILE} \
-C "cobalt-dev@googlegroups.com"
fi
}
function deploy_key() {
echo Copying SSH key to target, please use password \'raspberry\' at \
the login prompt.
# Remove previous host key
ssh-keygen -R ${QEMU_ADDRESS}
# Accept a new one
ssh-keyscan -p 8022 ${QEMU_ADDRESS} >> ~/.ssh/known_hosts
ssh-copy-id -i ${SSH_KEY_FILE} -p 8022 -o StrictHostKeyChecking=no pi@${QEMU_ADDRESS}
${RUN_ON_PI} echo ssh connection works
}
function wait_for_qemu() {
# Loop until SSH connection attempt responds with permission denied
while :; do
echo "waiting for SSH to be up"
cmd=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=3 -o BatchMode=yes \
localhost -p 8022 echo up 2>&1)
OUTPUT=$?
if [ $OUTPUT -eq 255 ]; then
if [[ $cmd == *"Permission"* ]] ; then
echo "SSH seems up"
break
fi
fi
echo $cmd_out
sleep 2
done
}
function get_shrink_script() {
wget -nc ${SHRINK_SCRIPT} -O ${STAGING_DIR}/raspbian-shrink
chmod +x ${STAGING_DIR}/raspbian-shrink
}
# Resize root partition to fill image size
function first_run_expand() {
${RUN_ON_PI} raspi-config --expand-rootfs
${RUN_ON_PI} reboot; sleep 1
kill_qemu
}
# Turn off swapfile
function disable_swap() {
${RUN_ON_PI} dphys-swapfile swapoff
${RUN_ON_PI} dphys-swapfile uninstall
${RUN_ON_PI} systemctl disable dphys-swapfile.service
}
# Clean up large footprint packages
function clean_unnecessary_packages() {
${RUN_ON_PI} apt-get purge -qy \
libraspberrypi-doc \
avahi-daemon
}
# Update APT sources and install required libraries
function run_apt_updates() {
${RUN_ON_PI} apt-get -qy update
${RUN_ON_PI} apt-get install -y --auto-remove libpulse-dev libasound2-dev \
libavformat-dev libavresample-dev
# We are pulling in new deps from above, so lets fully upgrade.
${RUN_ON_PI} apt-get -qy upgrade
}
function install_cron_entry() {
# Fully replaces crontab, which has no other entries anyway
${RUN_ON_PI} crontab /boot/crontab.txt
}
function pre_shrink_cleanup() {
${RUN_ON_PI} rm -rf /var/cache
${RUN_ON_PI} sync
}
# Run the shrink script
function shrink_image() {
tools_check
sudo ${STAGING_DIR}/raspbian-shrink ${IMG_FILE} \
${SHRUNK_IMG_FILE}
echo =====
echo Final shrunk image available at: ${SHRUNK_IMG_FILE}
}
function customize_on_qemu() {
run_qemu_backgrounded
wait_for_qemu
make_ssh_key
deploy_key
first_run_expand
run_qemu_backgrounded
wait_for_qemu
disable_swap
clean_unnecessary_packages
run_apt_updates
install_cron_entry
pre_shrink_cleanup
kill_qemu
}
function prepare_raspi_image() {
tools_check
fetch_image
mount_root
modify_root
unmount_root
mount_boot
modify_boot
get_kernel
unmount_boot
customize_on_qemu
get_shrink_script
shrink_image
}
# If script is executed in the subshell, launch the full
# customization script.
if [[ "${BASH_SOURCE[0]}" == "${0}" ]] ; then
echo "Running full image preparation script."
prepare_raspi_image
else
echo "Script is sourced in current shell, ready to execute individual steps."
fi