Category Archives: Raspberry Pi

Raspberry Pi read only SD card

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.