Connect USB device

When I first got in contact with Docker, I wanted to use it for everything! So when I picked up running again with my Garmin Forerunner 305, I wanted to be able to connect it to my computer after a run and automatically upload all my shiny new track-data to strava.

After creating the script for this (the details of which will be the content for a future post), I decided to put my new found Docker knowledge to the test...

The first thing that came to mind was to run udev within my container. After reading up on this, found out that this could lead to some race conditions between the container and the host, so I decided to look for something else.

In order to be able to any of what I wanted to do, pretty much regardless of whatever solution approach I used, I already found the following important prerequisites:

  • You must run your container with the --privileged argument to allow it to access the devices. Reason for this is that every time you connect a USB device, it gets a different number and I cannot give access to one specific device
  • You must mount the host directory /dev/bus/usb to the /dev/bus/usb directory within your container with the argument -v /dev/bus/usb:/dev/bus/usb when starting your container in order for your container to see the devices on the USB bus.

In order to be able to execute something whenever a specific device (in my case a Garmin Forerunner 305 with idVendor=091e and idProduct=0003) I decided to create a script using both inotifywait and lsusb to wait for devices to be (un)plugged and check if it was the device we are interested in.

The inotifywait will keep on listening to inodes create/delete events under the dev/bus/usb directory and will execute commands whenever an inode corresponding to a relevant device has been just created.

The basic outline for the script is the following:

#Set the idVendor/idProduct that we are interested in
idVendor="091e"  
idProduct="0003"

idString="${idVendor}:${idProduct}"


#Check if the device we are interested in is already connected
#before we start listing in a loop
devWasConnected=$(lsusb | grep "ID ${idString}" | wc -l)



inotifywait -r -m /dev/bus/usb -e CREATE -e DELETE | while read e  
do  
    #check if the relevant device is now connected, based on 
    #idVendor and idProduct codes
    devIsConnected=$(lsusb | grep "ID ${idString}" | wc -l)

    #Check if the new device plugged in is garmin   
    #this means that if it was not connected before, but it is 
    #connected now, we must perform the actions
    if [[ ( "$devWasConnected" == 0) && ( "$devIsConnected" == 1) ]]
    then
        ##Do all the stuff you want to do when the device connects
        ##e.g. in my case, download all data from garmin watch
        ##and upload new tracks to strava
    fi

    #Now update the wasConnected to the isConnected for the
    #next iteration
    devWasConnected=${devIsConnected}
done

So, there I had my solution: I could trigger the execution of parts of my script within my container whenever a USB device was connected and I did not have to rely on adding udev to my container. The only two packages I needed to install within an Ubuntu container are usbutils and inotify-tools.

Let me know if this script is useful to you or whether you have had alternative solutions! I know at least one other solution is possible by using udevadm monitor to listen to kernel udev events.