Thursday, June 15, 2017

Emulating the Raspberryp Pi with systemd container integration

If you use the Raspberry Pi a lot, you know all about the usual pitfalls, like a new image having ssh disabled, the resolution is really crappy and the image is stuffed full of useless nonsense that nobody who wants to do any real work with the Pi ever needs.

That means that usually you flash your card, you plug in your monitor, keyboard and pointy device, you run your raspi-config, you setup your wifi if you got a Pi 3 or Pi Zero W, you kick out all the useless stuff, you upgrade everything, you install the stuff you actually want to use... aaaand you watch a lot of youtube and do other goofing-off stuff while you wait for the Pi to churn through all the stuff with its tiny processor.
 Sometimes, while you're waiting for a new Pi being delivered, you sit on the flashed SD card for a few days until it gets here so you can get to the waiting in earnest. 

You know, I've got a crazy idea: What if you didn't need the actual Pi to be physically present to configure it? Sure, sure, you can do a lot with manual config edits and stuff. But installing and uninstalling stuff, try doing that with vim! (or emacs, if you're one of that crowd).

The short of it is: It's possible. It turns out there are two ways to do it: a container, or a fully-fledged emulator. It also turns out there are some pitfalls.

How hard can it be?

The former, being fairly simple, is easy to get to run, but has some limitations - if you see the message "unsupported syscall" while you're installing or uninstalling packages, it does not make you happy. Still, since it is the easier one and suitable for most tasks, It is the one I'll show you for now.

Setting the stage

First, you'll want to install a few packages:
# aptitude install qemu qemu-user-static systemd-container binfmt-support
You might also have to manually update things a bit:
# update-binfmts
but usually the install script will have done that for you.

Then, depending on whether you want to use an image or a pre-flashed sd card, you might have to fiddle around with loop devices a bit:
# losetup -f -P --show 2017-01-01-raspbian-jessie.img
That should leave you with several loop devices, including /dev/loop0 for the whole image, as well as /dev/loop0p1 and /dev/loop0p2, guess what those are?

You can then mount the loop device wherever you want it to live:
# mount /dev/loop0p2 -o rw /thisismypi
And you'll also need
# mount /dev/loop0p1 -o rw /thisismypi/boot
especially if you plan to update/upgrade packages that might rely on the stuff in /boot being where it belongs. If you want to work on a pre-flashed SD card, it would look something like this:
# mount /dev/sdd2 -o rw /thisismypi
# mount /dev/sdd1 -o rw /thisismypi/boot
You get the picture.

Transmogrifying the image

The next thing you have to do is cd into there and edit /thisismypi/etc/ and comment out everything in there. That's highly hardware-specific stuff the emulator can't stomach. If you get really weird errors on starting the container, check that you did that first. On that note, if you get weird stuff like no USB support after putting the card into your real Pi, check you UNcommented everything again.

Last but not least, we're planning on using a chroot (that's what a "container" is, when you scrape off the fancy convenience layers and buzzwords), so whatever's not inside /thisismypi won't be visible from the chroot. This is why we have to copy our ARM binary support magic into the container:
# cp /usr/bin/qemu-arm-static /thisismypi/usr/bin
If you are unsure what to copy over, check
update-binfmts --display | grep arm
for the correct name. If you really, really, really don't want anything in your image you don't absolutely need, copy it somewhere else and mount -b the path in.

Let's roll!

There! We're done! Time to jump into our new spaceship and let 'er rip!
# systemd-nspawn -D /thisismypi
Whut? Doesn't look different... Ok, let's try something.
# uname -a
Linux thisismypi 4.4.0-53-generic #74-Ubuntu SMP Fri Dec 2 15:59:10 UTC 2016 armv7l GNU/Linux
Ok, so we're still on Ubuntu, but our architecture is now armv7l. Bingo! Now, let's get crazy:
root@thisismypi:/# su pi
pi@thisismypi:/ $ exit

But does it WORK work?

# apt-get update
Oh! Strange errors! What happen? Somebody set us up the bomb!
Ok, we're not TOTALLY done yet. One thing I've identified so far is that /var/lock is actually a symlink to /run/lock on the Raspberry Pi, and that doesn't get created. So do a
# cd /run/ && mkdir lock
and apt should run fine, at least I didn't have any issues afterwards. Mind, they're most likely there, waiting for me to discover them, so proceed at your own risk!

What does work, what doesn't?

Removing packages works, raspi-config works, but doesn't offer all options. You can of course edit config files at leisure.

Installing packages, hummm... MOSTLY works, but it breaks stuff if it goes too far down onto machine level, which translates to "Your image is now trash". Installing nginx or php should be fine, but I wouldn't try updating kernel modules or other low-level stuff that relies on hardware-specific quirks. Always remember: You're still on your PC hardware, no matter how much it may look like a Pi right now!

Ok, what DOESN'T work? Well for starters, all the custom hardware of the Pi isn't there, so no GPIO, no I2C, no SPI, no other goodies. Then there's everything specific to the broadcom chip, like the bootloader, etc.

To reiterate, it's still running on the very box you're sitting in front of, only qemu gives it the magic powers to run code of a different architecture, and the chroot makes the code think the world ends at the mountpoint. 

What's the worst that could happen?

Now for the million dollar question: Will it run again on the real Pi? Why yes of course it will! The whole exercise would be a bit pointless if it didn't, wouldn't it now? Now, to answer that question bluntly, the worst that could happen is the image not booting. In that case, mount it up again, back up all the irreplaceable stuff you created there, and start over with a fresh image.

No comments:

Post a Comment