How to programattically determine the device name/basename of the root partition?

Ref: The following question
Drive name? What is the correct term for the "sda" part of "/dev/sda"?

Given:

  1. I have a system, (in this case a Raspberry Pi, but this could be relevant to any ‘nix system.)
  2. It is running a version of Linux and it can be assumed that all normal Linux commands work.
  3. The boot device can be either a SD card or a USB attached storage device.
  4. If booted from an attached storage device, the device "basename" is sd(x)
  5. If booted from a SD card, the device "basename" becomes something like "mm(xxxx)"

What I want to do:
I want to be able to programmatically, (in a shell script if possible) the kind of device it was booted from, and change certain characteristics based on what the boot device is.

Simple example:

Boot device = "mmxxxxx"
   Print "Booted from SD card!"
Boot device = "sda"
   Print "Booted from Attached Storage!"

What I want to do is extract the, (for want of a better term), major device type the root partition is derived from, (i.e. "sd", "mm", or whatever, depending on what device is mounted as the root partition.)

I suspect that I could, somehow, list the device that is mounted on root, without listing everything in mount, and then extract the first two letters after the final slash. . .

Asked By: Jim JR Harris

||

Mount point is controlled by systemd.

You can list the systemd mount unit files through:

systemctl list-units --type=mount --all

sample output:

  -.mount                       loaded    active   mounted Root Mount                                   
  boot-efi.mount                loaded    active   mounted /boot/efi
...

The Root partition is controlled by -.mount.

systemctl status -- -.mount

● -.mount - Root Mount
     Loaded: loaded (/etc/fstab; generated)
     Active: active (mounted) since Wed 2024-02-07 
      Where: /
       What: /dev/sdaX
       Docs: man:fstab(5)
             man:systemd-fstab-generator(8)

To extract device name :

systemctl status -- -.mount |grep -oP '(?<=What: ).*' |xargs basename

Better, as pointed @steeldriver in this comment:

systemctl show --value --property=What -- -.mount |xargs basename

man systemd.mount:

What: Takes an absolute path of a device node, file or other resource to mount.

Answered By: GAD3R

Here’s a Linux answer that doesn’t rely on systemd (requires /sys mounted for lsblk use and also the jq command to parse results).

Knowing that lsblk already includes all the logic to figure out the dependencies in its tree output, I wondered how it could be leveraged for the task. Using --inverse displays leafs first (but yet doesn’t enable to give a leaf as argument), in order to filter with the / mount point first. Then with --json one can iterate to find the result.

It outputs the final device name, bypassing any layer of LVM, LUKS etc. (tested with a system using / over LV over LUKS in a partition of the disk). Not tested with RAID (where I would expect multiple devices to be output, or this jq script to fail).

That’s a single long line:

lsblk --json --tree --inverse | jq -r '.blockdevices[] | if .mountpoints[] == "/" then ( .. | if .children? then empty else if .name then .name else empty end end ) else empty end'

or more easy to read:

lsblk --json --tree --inverse |
    jq -r '
.blockdevices[] |
    if .mountpoints[] == "/" then
        .. |
            if .children? then
                empty
            else
                .name // empty
            end
    else
        empty
    end
'

It takes the first match for / and will recurse (with ..) over objects, until they don’t include anymore a children object (also named "children", the only kind of child), meaning it’s the target device, and then display its name.

To get only the 2 first characters of the result, just append an additional | cut -c-2.

Answered By: A.B

Use df / to get the device for /.
You can filter by column (--output=source) and row (tail -1), too:

% df --output=source / | tail -1
/dev/mapper/sys-root
Answered By: U. Windl

Here’s a simple way. I have only test this on my own box and your mount command may have a different output.

boot_device=( $(mount | grep boot) )
if [[ $(echo "${boot_device[0]}" | grep -c sd) -eq 1 ]]
then
echo "Booted from attached storage"
else 
echo "Booted from SD card!"
fi

You could, instead of else, use an elif construct, for instance:

elif [[ $(echo "${boot_device[0]}" | grep -c mm) -eq 1 ]]
then
echo "Booted from SD card!"
else
echo "ERROR: Something has gone horribly wrong!"
fi

You’ve already accepted an answer, but since I spent a few minutes figuring out how to do this with the mount command, I’m posting it as an answer.

Answered By: Wastrel

All of the previously posted answers are wrong; until less than a year ago none of them would have worked on my system reliably.¹

Fundamentally the problem is you need the stable name of the device not the name of a device that would access it on this boot. Because of dynamic minor allocation, these aren’t the same thing.

This works:

ROOTDEV=`grep '^[^ ]* / ' /proc/mounts | sed 's/ .*$//'`
if [ "$ROOTDEV" = /dev/root ]
then    ROOTDEV=`sed 's/^.* root=([^ ]*).*$/1/' /proc/cmdline`
fi
case "$ROOTDEV" in
UUID=*) ROOTDEV=/dev/disk/by-uuid/${ROOTDEV#UUID=}
esac

What this does is extract the correct device name from /proc where it was preserved from being mounted by initrd, and if it’s not there gets it from the kernel’s command line.

If it’s still /dev/root after that you have some ancient system, and /dev/root should be correct. It was a symbolic link to the correct root partition in those days. The device major/minor numbers are patched into the kernel binary either at build time or by Lilo, and the system does not have dynamic boot pathways unless it was booted from removable media, in which / is a ramdisk and asking the question doesn’t make sense.

¹Systemd’s status is borked and yields the wrong answer. Scanning /dev for a device node that matches repeats systemd’s bug (I actually found the bug but could not fix it) and returns some device node that has the correct major/minor but won’t be correct on the next boot.

The lsblk command has a hard time with multipath.

The problem I’ve had with df its tendency to return something that works on this boot but not the next boot. I don’t know why df has this problem only for the root device but it’s been problematic for ages.

Answered By: Joshua