Detect USB mass storage device connection/disconnection within Docker container

Dear Toradex,

how can I list USB-connected mass storage devices within a Docker container, which were connected AFTER the container has been started?

Hardware and OS information:
I have a Verdin iMX8MP Q 4GB WB IT V1.1A embedded board together with Verdin Development Board V1.1E. The OS, which runs on the board is:
Linux 5.15.77-6.2.0+git.aa0ff7e3554e #1-TorizonCore SMP PREEMPT
If I plug-in a USB mass storage device to the base board, it gets automatically properly mounted into the file system into /media folder. If I unplug it, it gets unmounted.
The application we’re developing runs inside a Docker container. The OS inside the container is the same OS:
Linux 5.15.77-6.2.0+git.aa0ff7e3554e #1-TorizonCore SMP PREEMPT

Detailed description of the problem:
To list mass storage devices connected over USB port I use:
lsblk -p
This works great for those USB mass storage devices, which were connected to the base board at the time the contianer started. Here’s an output:

NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
/dev/sda            8:0    1  1.9G  0 disk
└─/dev/sda1         8:1    1  1.9G  0 part /mnt/USB DISK
/dev/sdb            8:16   1  3.7G  0 disk
└─/dev/sdb1         8:17   1  3.7G  0 part /mnt/UDISK
/dev/mmcblk2      179:0    0 29.1G  0 disk
└─/dev/mmcblk2p1  179:1    0 29.1G  0 part /etc/hosts
                                           /etc/hostname
                                           /etc/resolv.conf
                                           /mnt
/dev/mmcblk2boot0 179:32   0    4M  1 disk
/dev/mmcblk2boot1 179:64   0    4M  1 disk
/dev/mmcblk1      179:96   0  1.8G  0 disk
└─/dev/mmcblk1p1  179:97   0  1.8G  0 part /mnt/MT86PLUS
/dev/zram0        253:0    0    0B  0 disk

Once I disconnect one USB mass storage device, the output looks like this:

NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
/dev/sdb            8:16   1  3.7G  0 disk
└─/dev/sdb1         8:17   1  3.7G  0 part /mnt/UDISK
/dev/mmcblk2      179:0    0 29.1G  0 disk
└─/dev/mmcblk2p1  179:1    0 29.1G  0 part /etc/hosts
                                           /etc/hostname
                                           /etc/resolv.conf
                                           /mnt
/dev/mmcblk2boot0 179:32   0    4M  1 disk
/dev/mmcblk2boot1 179:64   0    4M  1 disk
/dev/mmcblk1      179:96   0  1.8G  0 disk
└─/dev/mmcblk1p1  179:97   0  1.8G  0 part /mnt/MT86PLUS
/dev/zram0        253:0    0    0B  0 disk

This is expected, the USB disk (/dev/sda) disappeared from the list.
Once I connect it back again, the output looks like this:

NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
/dev/sda            8:0    1  1.9G  0 disk
└─/dev/sda1         8:1    1  1.9G  0 part
/dev/sdb            8:16   1  3.7G  0 disk
└─/dev/sdb1         8:17   1  3.7G  0 part /mnt/UDISK
/dev/mmcblk2      179:0    0 29.1G  0 disk
└─/dev/mmcblk2p1  179:1    0 29.1G  0 part /etc/hosts
                                           /etc/hostname
                                           /etc/resolv.conf
                                           /mnt
/dev/mmcblk2boot0 179:32   0    4M  1 disk
/dev/mmcblk2boot1 179:64   0    4M  1 disk
/dev/mmcblk1      179:96   0  1.8G  0 disk
└─/dev/mmcblk1p1  179:97   0  1.8G  0 part /mnt/MT86PLUS
/dev/zram0        253:0    0    0B  0 disk

The /dev/sda (as well as /dev/sda1) appear in the list again, however MOUNTPOINTS column is blank.
Funny fact is, that the mass storage device is actually mounted, because a folder does appear for it in /mnt folder and I can browse it.
Note, that in the host operating system the lsblk works as expected. That is, no matter how many times I connect/disconnect USB mass storage devices, lsblk produces expected output.

What do I need it for?
I am writing a .NET 8.0 application, which will support data export functionality. If the user wants to perform data export, the application must list USB-connected mass storage devices and offer the user the one, where the data export will be stored.
In .NET I can use GetDrives() method of System.IO.DriveInfo class, however that method, too, seems to return only those “drives”, which were mounted at the time container started. The moment I disconnect a USB stick and re-connect it again, it won’t be returned in GetDrives() method. I’m assuming, this is related to the information returned by lsblk -p.
This forum post seemed relevant to the problem: Torizon Docker Container USB automount, but it did not help. I added these extra torizon packages to my container:

"usbview",
"usbutils",
"autofs",
"udisks2",
"systemd",
"udiskie"

but it did not help.

  • Is there a way to “fix” the output from lsblk ?
  • Is there a different way to retrieve the list of currently mounted USB mass storage devices?
    • I obviously do not want to do it by listing the content of /mnt folder. Besides the primary reason, which is, that it is not a way to do it, we want to give the user some more information about currently attached USB mass storage devices (such as the amount of free space).
  • Are there maybe further Debian packages, which I should include for the container, that could fix the problem or provide me with the functionality I’m looking for?
    • For exampe, I was experimenting with output from lsusb, giving it different command line arguments, but it does not produce output I’m looking for.

Thank you!

Best regards
Vladimir Gregor
Bee Mobile company

Greetings @GMD,

Now this is a bit tricky. If you want to see the mount-points of the host system then you’ll need to be a bit insecure with how you run your container. To explain, what you’re essentially asking for here is, for the container to have access to the same mount namespace as the host system.

The mount namespace is one of many namespaces in the Linux kernel. This one provides isolation of the file system mount points seen by a group of processes. Due to the inherent isolation of containers, containers by default get their own mount namespace and can’t access the host mount namespace.

Now we can break this isolation, but it will result in a fairly “insecure” container at least by normal container standards. First run a container like so:

docker run --rm -it --privileged --pid host torizon/debian:3-bookworm nsenter -t 1 -m bash

We’re running a privileged container with the same PID as the host system. We then use the nsenter command to give access to the mount namespace of PID 1 to the bash process in the container. With this here’s what we see in the container:

# USB Drive already attached (sda1)
bash-5.1# lsblk
NAME         MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda            8:0    1 29.1G  0 disk
`-sda1         8:1    1 29.1G  0 part /var/rootdirs/media/COSTCO_USB
mmcblk0      179:0    0 14.8G  0 disk
`-mmcblk0p1  179:1    0 14.8G  0 part /var
                                      /usr
                                      /boot
                                      /
                                      /sysroot
mmcblk0boot0 179:32   0 31.5M  1 disk
mmcblk0boot1 179:64   0 31.5M  1 disk
zram0        253:0    0    0B  0 disk
# Remove USB drive
bash-5.1# lsblk
NAME         MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
mmcblk0      179:0    0 14.8G  0 disk
`-mmcblk0p1  179:1    0 14.8G  0 part /var
                                      /usr
                                      /boot
                                      /
                                      /sysroot
mmcblk0boot0 179:32   0 31.5M  1 disk
mmcblk0boot1 179:64   0 31.5M  1 disk
zram0        253:0    0    0B  0 disk
# Reattach USB drive
bash-5.1# lsblk
NAME         MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda            8:0    1 29.1G  0 disk
`-sda1         8:1    1 29.1G  0 part /var/rootdirs/media/COSTCO_USB
mmcblk0      179:0    0 14.8G  0 disk
`-mmcblk0p1  179:1    0 14.8G  0 part /var
                                      /usr
                                      /boot
                                      /
                                      /sysroot
mmcblk0boot0 179:32   0 31.5M  1 disk
mmcblk0boot1 179:64   0 31.5M  1 disk
zram0        253:0    0    0B  0 disk

As you can see attaching and reattaching my USB drive works and lsblk gives proper output with the MOUNTPOINTS listed properly. At the moment I’m not sure how to get the same result while running the container more securely.

Here’s some references I found when doing my research:

Hopefully this was of some help to you.

Best Regards,
Jeremias

2 Likes

Just wanted to add an additional comment because I just had another idea that is arguably more secure then my initial suggestion. You could run a container with --network=host. Then install an ssh client in the container. Using this you can do ssh torizon@localhost in the container and it will let you ssh into the host system outside of the container. From there you could extract the information you need.

Now whether this is actually more secure or not in practice, not sure. I mean with this method you don’t have to run a privileged container with the same PID as the host. But you can still break out of the container now with ssh. Though as I said, some breakage of container isolation is probably required here.

Best Regards,
Jeremias

1 Like

Thank you, jeremias.tx! I was able to verify your advice and I confirm, that it works!

I have a slightly different problem now, not completely related to the original question, but I’m gonna try to task it anyway.
My problem is, that I do not run the container with docker run command, but rather with docker compose command. Or, to be honest, I’m fairly new to docker, and I don’t know really, what I’m doing - I only let the scripts run, which were generated by Visual Studio Code by Torizon IDE Extension.

So the question really is, which build task should I alter so that the container runs in the privileged mode?

Here’s an example of task: run-container-torizon-debug-arm64:

DOCKER_HOST=192.168.1.28:2375
DOCKER_LOGIN=192.168.1.28:5002
TAG=arm64
GPU=-vivante
docker compose -p torizon up -d file-system-tester

Or should I maybe update/change docker-compose.yml file? If so, how?
Or should I maybe update/change Dockerfile (Dockerfile.debug) file?

I was able to verify your advice but running my existing container (which already happens to be uploaded the Torizon Board) by executing lsblk -p commands and as mentioned, things work as expected. However, if I run my container with docker run ... command, my .NET application is not in the container, because, I’m assuming, it is uploaded into the container during the “docker compose …” process.

Thanks a lot either way!

Best regards
Vladimir Gregor
Bee Mobile company

Thank you, jeremias.tx! I was able to verify your advice and I confirm, that it works!

Good to hear!

So the question really is, which build task should I alter so that the container runs in the privileged mode?

You want to modify your docker-compose.yml file in this case. This dictates how your container is launched similar to a docker run command. For details on how to add certain arguments or achieve certain things in your compose file I would recommended referencing the Docker documentation on this subject: Compose file version 3 reference | Docker Docs

Best Regards,
Jeremias

1 Like

Thank you Jeremias, I’ll try to look into it. You’ve been very helpful.

Best regards
Vladimir Gregor
Bee Mobile company