This will be my first post about a project with the Raspberry PI. I wanted to build an internet radio but I was afraid to corrupt the file system on the SD card when I just power off the radio without shutting it down correctly. Also it is necessary to avoid extensive writing to SD cards because flash cells do not like too many writes. After that procedure you will not be able to permanently write to the SD card anymore. All writes will end up in the RAM and will be lost after reboot. If your project needs to write to files you will have to add another data partition to your SD card or you have to add an USB stick. Also do not do things like extensive logging, because this will fill up your RAM! It’s also worth spending a little bit more money for the SD card. Cards like a SanDisk Ultra or even SanDisk Extreme will speed up things quite a bit and they are also more reliable than no name SD cards. For most projects 8GB are also more than enough.
I found some hints on the internet but non of them really worked with the newest Debian “jessie” based image. So I spend a weekend to get it to work – and to make sure I can find it again I write the steps down here.
Before you start this procedure I would recommend that you start with a small image “RASPBIAN JESSIE LITE” from the rasbian download page. Then add all packages which are needed for your project. This will make sure that your image stays small and that the boot time is not longer than necessary. During development at least I do try this and that until everything is working. This will leave a lot of “garbage” in your system. That’s why I always would start with a new “clean” image after development is finished. There you only add what is really needed. So now lets assume you have a system which is working fine and you want to write protect your SD card now.
Lets make sure Debian is up to date:
apt-get update apt-get upgrade
Now we want to update the kernel and the firmware:
apt-get install rpi-update rpi-update reboot
Now we want to get rid of some things we no most probably will not need in our “embedded system”:
apt-get remove --purge triggerhappy cron anacron logrotate
We dont’t need normal rsyslog on most appliances – that’s why we replace it with the busybox.
apt-get install busybox-syslogd dpkg --purge rsyslog
We also want to avoid swapping so we disable it:
dphys-swapfile swapoff dphys-swapfile uninstall update-rc.d dphys-swapfile remove
To speed up booting we disable some things:
systemctl disable bootlogs.service systemctl disable console-setup.service systemctl disable exim.service
On an appliance we want to make sure that it always runs. If something goes wrong we do not want it to get stuck. We better restart the system.
First we set some kernel parameters which will do the following:
- vm.panic_on_oom = 1 -> will create a kernel panic if system is out of memory
- kernel.panic = 10 -> will automatically reboot the system after 10 seconds in case of kernel panic
so we add the following lines to /etc/sysctl.conf
echo "# panic kernel on OOM" >> /etc/sysctl.conf echo "vm.panic_on_oom=1" >> /etc/sysctl.conf echo "# reboot after 10 sec on panic" >> /etc/sysctl.conf echo "kernel.panic=10" >> /etc/sysctl.conf
The Broadcom SOC in the raspi does have also a hardware watchdog which we can enable. This will make sure that the system restarts if it becomes unresponsive in any way. To do that we have to do the following:
First we make sure the kernel module for the watchdog gets loaded at boot time. Therefore we add the following line to /etc/modules
echo bcm2708_wdog >> /etc/modules
Now we install the watchdog package:
apt-get install watchdog
Before we can start the watchdog we have to uncomment 2 lines:
- max-load-1 = 24 # that tells the watchdog to reboot when the load average 1min is higher than 24 may be on your system it has to be set higher or lower
- watchdog-device = /dev/watchdog # because we do have the hardware watchdog
perl -p -i -e 's/#max-load-1/max-load-1/' /etc/watchdog.conf perl -p -i -e 's/#watchdog-device/watchdog-device/' /etc/watchdog.conf
After that is done theoretically could start the service. But I found a bug in the unit file. To fix that we have to add WantedBy=multi-user.target to the [Install] section of /lib/systemd/system/watchdog.service
perl -p -i -e 's/\[Install\]/[Install\]\nWantedBy=multi-user\.target/' /lib/systemd/system/watchdog.service
Now we can enable and start watchdog
modprobe bcm2708_wdog systemctl enable watchdog.service systemctl start watchdog.service
Now it’s time to reboot.
Up to now you should be sure that everything really works as expected because we now will make the file system on the SD card read only.
amp;
first we have to install UnionFS which is an overlaying filesystem (you can mount from several different locations into the same folder). In case of collisions, UnionFS uses priorities for the filesystems. It is frequently used to create RAM-Disk Overlays for read-only systems, for instance also with Live CDs.
apt-get install unionfs-fuse
In the next step we will create a mount script in //usr/local/bin/mount_unionfs …
#!/bin/sh DIR=$1 ROOT_MOUNT=$(awk '$2=="/" {print substr($4,1,2)}' < /etc/fstab) if [ $ROOT_MOUNT = "rw" ] then /bin/mount --bind ${DIR}_org ${DIR} else /bin/mount -t tmpfs ramdisk ${DIR}_rw /usr/bin/unionfs-fuse -o cow,allow_other,suid,dev,nonempty ${DIR}_rw=RW:${DIR}_org=RO ${DIR} fi
… and make it executable.
chmod +x /usr/local/bin/mount_unionfs
In the next Step we prepare some directories.
cp -al /etc /etc_org mv /var /var_org mkdir /etc_rw mkdir /var /var_rw
Just to be sure we save the old fstab:
cp /etc/fstab /etc/fstab_rw
Now we modify the /etc/fstab to the following contents:
proc /proc proc defaults 0 0 /dev/mmcblk0p1 /boot vfat ro 0 2 /dev/mmcblk0p2 / ext4 ro,noatime 0 1 mount_unionfs /etc fuse defaults 0 0 mount_unionfs /var fuse defaults 0 0 none /tmp tmpfs defaults 0 0
We copy that as well:
cp /etc/fstab /etc/fstab_ro
And now its time to reboot again.
If everything has worked the output of the command mount should look like this:
/dev/mmcblk0p2 on / type ext4 (ro,noatime,data=ordered) devtmpfs on /dev type devtmpfs (rw,relatime,size=469760k,nr_inodes=117440,mode=755) sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) proc on /proc type proc (rw,relatime) tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev) devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000) tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755) tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k) tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755) cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset) cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct) cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio) cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices) cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer) cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls) systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct) debugfs on /sys/kernel/debug type debugfs (rw,relatime) mqueue on /dev/mqueue type mqueue (rw,relatime) configfs on /sys/kernel/config type configfs (rw,relatime) fusectl on /sys/fs/fuse/connections type fusectl (rw,relatime) none on /tmp type tmpfs (rw,relatime) /dev/mmcblk0p1 on /boot type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-ro) ramdisk on /var_rw type tmpfs (rw,relatime) ramdisk on /etc_rw type tmpfs (rw,relatime) unionfs-fuse on /var type fuse.unionfs-fuse (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other) unionfs-fuse on /etc type fuse.unionfs-fuse (rw,relatime,user_id=0,group_id=0,default_permissions,allow_other)
That’s it 🙂
If you want to do changes now put the SD Card into another computer and copy the fstab_rw over the changed fstab file and boot again in the rspi.