Locating an SD card

This is part of a series of posts on the design and technical steps of creating Himblick, a digital signage box based on the Raspberry Pi 4.

The first step in writing a script to setup an SD card image, is to locate the SD card device under /dev/, since we don't want to manually locate the SD card device every time we set one up.

Years ago, this kind of thing used to be unreasonably hard. Luckily, now it's 2019, and we have lsblk, which can even output JSON:

    def locate(self) -> Dict[str, Any]:
        """
        Locate the SD card to work on

        :returns: the lsblk data structure for the SD device
        """
        res = subprocess.run(["lsblk", "--json", "--output-all", "--bytes"], text=True, capture_output=True, check=True)
        res = json.loads(res.stdout)
        devs = []
        for dev in res["blockdevices"]:
            if dev["rm"] and not dev["ro"] and dev["type"] == "disk" and dev["tran"] == "usb":
                devs.append(dev)
                log.info("Found %s: %s %s %s %s",
                         dev["path"], dev["vendor"], dev["model"], dev["serial"],
                         format_gb(int(dev["size"])))
        if not devs:
            raise Fail("No candidate SD cards found")
        if len(devs) > 1:
            raise Fail(f"{len(devs)} SD cards found")
        return devs[0]

I would also have wanted to check for not dev["rota"], but for some sad reason unknown to me, in my system the SD card is showing as a rotational block device.

That code should be sufficient to guarantee that I don't end up accidentally dumping a raspbian image over the SSD of my laptop, although it would still risk detecting my USB-mounted backup disk as an SD card.

Still, if I insert the SD card to burn while the backup disk is plugged in, I should still get the failure of 2 SD cards present.

A very good outcome of using lsblk -JOb is that I do not only get the device name, but also a nice data structure with device information, including a list of all partitions, which will come in very handy later.

For example, it becomes trivial to unmount the SD partitions in case they were automounted by default on insertion:

    def umount(self, dev: Dict[str, Any]):
        """
        Make sure all the SD partitions are unmounted

        :arg dev: the lsblk data structure for the SD device
        """
        for part in dev["children"]:
            mp = part["mountpoint"]
            if not mp:
                continue
            subprocess.run(["umount", mp], check=True)