USB device cannot be viewed by docker container (+ libusb)

Hi there !

I came upon discovering openBalena a month ago, and though it was a good idea to deploy Raspberries : It was true !
Anyways, now that I have some PIs powered on and open-balena-admin to check everything, I wanted to implement some the docker containers that my teacher has made : a simple USB to CAN bus interface that get values about thermometers and a web dashboard that listen on port 80 (this seems to be working fine, so I’m skipping it in the rest of the post).
I do not have the knowledge on how in depth Docker or how the CAN bus works, that’s why I’m trying my best to be clear.

Previously, we just had to flash a simple Raspberry PI OS from here on our RaspberryPI4 and used the following commands :

docker pull somewhere.net/image:latest
docker create --network=bridge -p 9201:9201 -p 9202:9202/udp --name=bridgecan --privileged --restart=unless-stopped -v /dev/bus/usb:/dev/bus/usb -v /etc/localtime:/etc/localtime --log-driver json-file --log-opt max-size=20m --log-opt max-file=5

Since openBalena images are deployed via a compose file, I have ‘translated’ the previous command into this :
docker-compose.yml

version: ‘2’
networks:
bridge:
driver: bridge
services:
bridgecan:
restart: unless-stopped
privileged: true
devices:
- /dev:/dev
ports:
- “9201:9201”
- “9202:9202/udp”
#logging:
# driver:
# - json-file
# options: “max-size=20m,max-file=5”
networks:
- bridge
environment:
- UDEV=on
image: somewhere.net/image:latest

There is no volumes in the abose docker-compose, because if I tried I would get the following:

root@openbalena-server:/home/balena/images# bash build.sh
[debug] new argv=[/home/balena/balena-cli/balena,/snapshot/versioned-source/bin/balena,deploy,Pi4Generique,–registry-secrets,secret.yaml] length=6
[debug] Deprecation check: 0.15350 days since last npm registry query for next major version release date.
[debug] Will not query the registry again until at least 7 days have passed.
[debug] Event tracking error: Response code 404 (Not Found)
[Debug] Parsing input…
[Debug] Loading project…
[Debug] Resolving project…
[Debug] docker-compose.yml file found at “/home/balena/images”
[Debug] Creating project…
[Error] Deploy failed
Bind mounts are not allowed

I tried the workaroud that I found in the forums and in github : adding /dev:/dev in the devices sections of the compose file.
Now the balena deploy command doesn’t seems to be reluctant to send the image to the Raspberries.
When I’m logging via SSH to one of the PIs, I can see that the container exist !

Now that it’s running, I’m pluging the microcontroller to the RaspberryPi, here are some informations that I got.

Logs when plugin the said microcontroller :

HotPlug Event : Device Plugged; Connected=0
libusb: error [_get_usbfs_fd] File doesn’t exist, wait 10 ms and try again
libusb: error [_get_usbfs_fd] libusb couldn’t open USB device /dev/bus/usb/001/005: No such file or directory
No Device Found
HotPlug Event : Device Unplugged; Connected=0

The container’s POV of /dev/bus/usb/001 :

root@e8710465090e:~/libusb-1.0.22# ls -lah /dev/bus/usb/001
total 0
drwxr-xr-x 2 root root 100 Apr 19 08:22 .
drwxr-xr-x 4 root root 80 Apr 19 08:22 …
crw-rw-r-- 1 root root 189, 0 Apr 19 08:22 001
crw-rw-r-- 1 root root 189, 1 Apr 19 08:22 002
crw-rw-r-- 1 root root 189, 2 Apr 19 08:35 003

The host’s POV of /dev/bus/usb/001 :

root@b68e84b:~# ls -lah /dev/bus/usb/001
total 0
drwxr-xr-x 2 root root 100 Apr 19 08:35 .
drwxr-xr-x 4 root root 80 Jan 1 1970 …
crw-rw-r-- 1 root root 189, 0 Apr 19 08:00 001
crw-rw-r-- 1 root root 189, 1 Apr 19 08:00 002
crw-rw-r-- 1 root root 189, 4 Apr 19 08:35 005

So I was wondering multiple things :

  • Why does the devices in the container (/dev/bus/usb) is not updated and how to resolve this issue?
    As I understand it, the container need to have access to files inside to properly communicate with the devices.
  • Why does the docker-compose.yml doesn’t allow me to bind the volume /dev/bus/usb:/dev/bus/usb directly ? Wouldn’t it be easier and/or faster ?

If you need anything else, or if I encounter something new, I will be glad to answer in this post.
Thanks you for your time, hope you are having a good day !

Why does the devices in the container (/dev/bus/usb) is not updated and how to resolve this issue?

Devices files are populated in Linux using the userspace udev daemon. You can run this daemon in your container to have these devices be automatically populated after hotplug, and you can read more about this here:

Why does the docker-compose.yml doesn’t allow me to bind the volume /dev/bus/usb:/dev/bus/usb directly ? Wouldn’t it be easier and/or faster ?

I did some digging. I’m still not completely sure of the basis for this, but as I understand it, the reason for forbidding bind mounts is to prevent applications from tampering with the host OS in a way that could render the system unbootable or unable to be upgraded.

Additionally, bind mounting anything from the /dev directory can alter the permissions of devices on the host when accessed from inside a privileged container, making them inaccessible by the OS. For this reason, it’s generally better to create the device nodes inside your container when possible.

Thanks @jakogut … I had a similar issue trying to communicate with a USB UPS …

Just added the “ENV UDEV=1” statement into my dockerfile and all was good (I was already in privileged mode!) …

Glad you got it to work @malharwood! Thanks for letting us know!

Hello again
I was able to add ENV UDEV=on in they dockerfile, and npw the Docker works, thanks @jakogut !
However, it works only if the device was already plugged during the container initialisation.

If the device is already connected : I can communicate with it
If the device is hotpluged : nothing seems to be happening
If the device is removed after it was successfully recognised by the container during it’s start : disconnect like intended

Here are some things that I think might be usefull : udevadm is not the same on the host’s POV and the container’s POV.

Host POV :
udevadm have two processes :
/lib/systemd/systemd-udevd PID 1051
/lib/systemd/systemd-udevd --daemon PID 8695
Results of udevadm monitor when hotplugging :

KERNEL[1242.974614] add      /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4 (usb)
KERNEL[1242.981194] add      /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0 (usb)
KERNEL[1242.981399] bind     /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4 (usb)
UDEV  [1242.991131] add      /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4 (usb)
UDEV  [1243.000923] add      /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0 (usb)
UDEV  [1243.007316] bind     /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4 (usb)

Container POV :
udevadm has only one process, child of ENTRYPOINT
/lib/systemd/systemd-udevd --daemon PID 29
Results of udevadm monitor when hotplugging :

KERNEL[1418.580558] add      /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4 (usb)
KERNEL[1418.582972] add      /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4/1-1.4:1.0 (usb)
KERNEL[1418.585362] bind     /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.4 (usb)

Does the fact that UDEV lines are not appearing the container means that it cannot ‘see’ the device ?
Again, thanks for the answers that you gave me earlier !
It seems to make me move forward, even though it’s still not perfect.

What is ‘ image: somewhere.net/image:latest’? UDEV auto mount relies on BalenaLib images which contain some scripts for USB mounting to work. Non-Balena images might not work without some modification. Can drop the /dev:/dev mount, don’t think it is necessary with the UDEV label or env var and may cause complications. And may also need the privileged flag added too.

1 Like

Hi @Maggie
Thanks for the advice, it now is working !

somewhere.net/image:latest was an image of a private respository.
It was an almost clean version of ubuntu 20.04, just some packages where added.
I changed it to balenalib/raspberrypi4-64 as base and redownloaded everything directly in the dockerfile : now when hotplugging, the device is recognised and I don’t have any more problem !

Thanks everyone for your help.
I suppose that I have to close this topic then ? Or someone with higher permission have to do it ?

1 Like