Init a raspberry pi on boot without anything !

September 22, 2018

Purpose

8 years ago, when I started working at Teevity, I discovered Amazon Web Services and a special init script for ubuntu images called "cloud init".
Let's see what we can do with this and how to do the same with a raspberry pi.

What is Cloudinit ?

It was built for Amazon EC2 instances. Now it is available for all cloud providers including OpenStack :

Custom scripts are attached to instances to perform specific actions when the instance is launched. For example, if you are unable to install cloud-init inside a guest operating system, you can use a custom script to get a public key and add it to the user account. Type your script directly into the Customization Script field. If your browser supports the HTML5 File API, you may choose to load your script from a file. The size of your script should not exceed 16 Kb.

Cloud init is executed as the last step in the boot process. It calls the hypervisor to get metadata containing the script you typed. IE on Amazon EC2 it calls http://169.254.169.254 (check Instance Metadata and User Data documentation) In many cases it can helps a lot for automation : no custom images, a good init script can install an entire instance with all your softwares without deploying orchestration tools such as ansible or salt.
INFO : Ansible, Juju, etc... are great and usefull tools

Cloud init for raspberry pi ??? why ???

It is not a cloud init, but it tooks inspiration from. Build a custom image for a raspberry PI is not so easy to do.

For special projects, we need to build some prototypes with special configurations, easy and fast to deploy. I mean "easy" because we do not have any connection, so no SSH, no screen and no keyboard.

SD card structure

Working with a machine running Linux remove a big constraint : ext4 partitions are readables. With Windows or MacOS it could be a little different. On Raspbian SD card we have 2 partitions :

Raspbian has a feature called "headless" (https://www.raspberrypi.org/documentation/configuration/wireless/headless.md) for an easy setup. In other words, with 2 files we can connect to a wifi network and install a SSH server. Just put a wpa_supplicant.conf file directly in the /boot partition and an empty file "ssh" GREAT ! And a custom script ? No way they say...

Edit raspbian image

From : https://www.raspberrypi.org/forums/viewtopic.php?t=28860

 
    root@server:/tmp# fdisk -l 2018-06-27-raspbian-stretch-lite.img
    Disk 2018-06-27-raspbian-stretch-lite.img: 1,8 GiB, 1862270976 bytes, 3637248 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0x4d3ee428
    
    Device                                Boot Start     End Sectors  Size Id Type
    2018-06-27-raspbian-stretch-lite.img1       8192   96663   88472 43,2M  c W95 FAT32 (LBA)
    2018-06-27-raspbian-stretch-lite.img2      98304 3637247 3538944  1,7G 83 Linux
    root@server:/tmp# mount -v -o offset=50331648 -t ext4 2018-06-27-raspbian-stretch-lite.img ./root
    
    

I edited rc.local file (/etc/rc.local) to find a start.sh script in /boot and execute it if exists :

 
        #!/bin/sh -e
        #
        # rc.local
        #
        # This script is executed at the end of each multiuser runlevel.
        # Make sure that the script will "exit 0" on success or any other
        # value on error.
        #
        # In order to enable or disable this script just change the execution
        # bits.
        #
        # By default this script does nothing.

        # Print the IP address
        _IP=$(hostname -I) || true
        if [ "$_IP" ]; then
        printf "My IP address is %s\n" "$_IP"
        fi

        if [ -f /boot/start.sh ]; then
        printf "Launching a startup script"
        /boot/start.sh
        fi

        exit 0
    
    

Start script

I have two distinct tasks :

To make a distinction, I simply use an empty file as a flag while the installation is fresh. If the file exists, I need to install.

 
        #!/bin/sh
        
        if [ -f /boot/initial ]; then
            apt-get update
            apt-get install -y python python-pip python-numpy ca-certificates
            pip install RPi.GPIO
            pip install requests
            rm /boot/initial
        fi
        
        # try to download a new version
        wget http://admin:PASSWORD@downloads.mathieupassenaud.fr/mystuff.py -O /opt/mystuff.py
        python /opt/mystuff.py &
    

So I have now a generic raspbian image. With a simple copy/paste of a set of files (wpa_supplicant.conf, start.sh, initial). With some projects (more to come next month !) I put some configuration files. Anyone can deploy a software without SSH, mounting EXT4 partition... Just copy/paste files directly with the mouse in Windows :-)

My image is available here