Skip to content

Instantly share code, notes, and snippets.

@adeutscher
Last active July 11, 2021 18:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adeutscher/73073f90566cccd8c79b633ca2139a5a to your computer and use it in GitHub Desktop.
Save adeutscher/73073f90566cccd8c79b633ca2139a5a to your computer and use it in GitHub Desktop.
Exploration of using pyudev for hotplug events.
#!/usr/bin/env python
'''
A bit of exploration kicked off by this example: https://avilpage.com/2016/09/detecting-device-events-in-ubuntu-with-python.html
Useful documentation links:
* https://pyudev.readthedocs.io/en/v0.12/api/device.html
* https://pyudev.readthedocs.io/en/v0.13/api/monitor.html
* https://pyudev.readthedocs.io/en/latest/guide.html
Other:
* usb_manager: https://github.com/M-Gregoire/usb_manager
ToDo items:
* In general, handle more types of events and devices.
* Information gathering:
* For USB devices, proper collection of manufacter information/etc. Inspiration had model and vendor.
* For NICs, printing of MAC addresses (/sys/class/net/{interface}/address in the worst case?)
* Author of
* Confirm behavior for different types of network events (bridges, attaching of interfaces to bridges)
* This could also help me to have a solution for an ancient though minor problem that's been bugging me about Bluetooth tethering: https://www.reddit.com/r/linuxquestions/comments/3ny4mq/android_tethering_troubleshooting/
* Filter out the redundant stuff
* Rig up output.
* For general distribution, directly invoking notify-send would probably be appreciated
* My personal version of this would probably punt the message over UDP to be used by https://github.com/adeutscher/core-tools/blob/master/scripts/networking/simple-message-servers/desktop-notify-relay.py.
'''
# pip install --user pyudev usb_manager
from __future__ import print_function
import pyudev # For detecting events
# from usb_manager import UsbManager # For displaying info, experimental
context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
# Note: Though the phrasing in documentation worried me, these do stack (e.g. filtering by net doesn't override filtering by USB)
monitor.filter_by(subsystem='usb') # Display information if a USB device was added.
monitor.filter_by(subsystem='net') # Display information if a network interface was added.
monitor.filter_by(subsystem='block') # Block devices (seems to just be storage disks and partitions?)
# Another subsystem type would be 'input'.
for device in iter(monitor.poll, None):
# For more information on devices: https://pyudev.readthedocs.io/en/v0.12/api/device.html
print('%s (action: "%s"). Device is a %s (driver: %s, subsystem: %s)' % (device, device.action, device.device_type, device.driver, device.subsystem))
if device.action == 'add':
# Do the thing.
'''
You may want to immediately filter out items with device_type values of 'usb_device' here.
Leaving it in for demo purposes, but it seems like it iss not very helpful because it happens
for everything and a more specific message is just around the corner.
'''
if device.subsystem == 'usb':
if device.device_type == 'usb_device':
print('This is a USB device.') # Very redundant
if device.device_type == 'usb_interface':
# from 'usb' filter
# Note: 'interface' wording does not imply a NIC. Oops.
'''
Still working on getting human-readable manufacturer information.
This could be a solution, but I'm not 100% happy with it
as it involves grabbing and skimming through lsusb output:
https://stackoverflow.com/questions/8110310/simple-way-to-query-connected-usb-devices-info-in-python
Experimented with usb_manager, but it didn't work entirely right. Probably programmer error, Next session.
There must be a tidy way to do this just in Python, but I've made progress for now and need food.
'''
if device.driver == 'usb-storage':
print('Storage media.')
'''
For a NIC, driver will be specific to the one used by the NIC
(e.g. wireless would be different than wired, and different
interfaces of the same type wouldn't necessarily have the
same driver)
'''
elif device.subsystem == 'net':
if device.device_type == 'wlan':
print('Wireless NIC: %s' % device.sys_name)
elif device.device_type is None:
print('Wired NIC: %s' % device.sys_name)
elif device.subsystem == 'block':
# 'Block' is specific to disks?
# This did not trigger with a serial adapter.
print('New block device: %s (%s)' % (device.device_node, device.device_type))
else:
print('Unaccounted-for event (subsystem: %s, device_type: %s)' % (device.subsystem, device.device_type))
elif device.action == 'delete':
# A bit confused by delete. It would happen until I started filtering by multiple device types.
# Haven't looked too far into it yet.
print('{} removed. Device was a %s (%s)' % (device, device.device_type, device.driver))
else:
print('Did not account for a %s action on %s' % (device.action, device))
'''
Some example output.
* For each, I've inserted a note about removal points
* Input may have been taken from a slightly old
USB Storage device:
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3') connected. Device is a usb_device (usb)
This is a USB device.
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0') connected. Device is a usb_interface (usb-storage)
Storage media.
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/host6/target6:0:0/6:0:0:0/block/sdb') connected. Device is a disk (None)
New block device: /dev/sdb (disk)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/host6/target6:0:0/6:0:0:0/block/sdb/sdb1') connected. Device is a partition (None)
New block device: /dev/sdb1 (partition)
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/host6/target6:0:0/6:0:0:0/block/sdb/sdb1')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/host6/target6:0:0/6:0:0:0/block/sdb')
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
USB Wireless NIC:
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1') connected. Device is a usb_device (usb)
This is a USB device.
Unaccounted-for device type: usb_device
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0') connected. Device is a usb_interface (rt2800usb)
Looks like a USB NIC
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/net/wlan1') connected. Device is a wlan (None)
Wireless NIC: wlan1
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0')
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1')
--- Removal ---
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/net/wlan1')
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0')
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1')
USB Wired NIC (did not work as I expected, but apparently documentation at https://pyudev.readthedocs.io/en/v0.12/api/device.html
expects regular ethernet devices to have a type of None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3') connected. Device is a usb_device (usb)
This is a USB device.
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0') connected. Device is a usb_interface (asix)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/net/eth1') connected. Device is a None (None)
Wired NIC: eth1
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
--- Unplug ---
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/net/eth1')
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
Serial Adapter
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3') connected. Device is a usb_device (usb)
This is a USB device.
Unaccounted-for device type: usb_device
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0') connected. Device is a usb_interface (ftdi_sio)
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
--- Removal ---
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
HDMI Cable (via a mini-DisplayPort-to-HDMI-adapter, though the script only piped up once I plugged in the HDMI cable):
Device(u'/sys/devices/pci0000:00/0000:00:02.0/drm/card0') (action: "change"). Device is a drm_minor (driver: None, subsystem: drm)
Did not account for a change action on Device(u'/sys/devices/pci0000:00/0000:00:02.0/drm/card0')
Simple USB Mouse (note: At this point I'd temporarily commented out filters in order to find more subsystem labels.)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1') (action: "add"). Device is a usb_device (driver: usb, subsystem: usb)
This is a USB device.
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0') (action: "add"). Device is a usb_interface (driver: usbhid, subsystem: usb)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001') (action: "add"). Device is a None (driver: hid-generic, subsystem: hid)
Unaccounted-for event (subsystem: hid, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22') (action: "add"). Device is a None (driver: None, subsystem: input)
Unaccounted-for event (subsystem: input, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22/mouse2') (action: "add"). Device is a None (driver: None, subsystem: input)
Unaccounted-for event (subsystem: input, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/hidraw/hidraw0') (action: "add"). Device is a None (driver: None, subsystem: hidraw)
Unaccounted-for event (subsystem: hidraw, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22/event17') (action: "add"). Device is a None (driver: None, subsystem: input)
Unaccounted-for event (subsystem: input, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001') (action: "bind"). Device is a None (driver: hid-generic, subsystem: hid)
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0') (action: "bind"). Device is a usb_interface (driver: usbhid, subsystem: usb)
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1') (action: "bind"). Device is a usb_device (driver: usb, subsystem: usb)
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1')
--- Removal ---
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22/mouse2') (action: "remove"). Device is a None (driver: None, subsystem: input)
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22/mouse2')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22/event17') (action: "remove"). Device is a None (driver: None, subsystem: input)
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22/event17')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22') (action: "remove"). Device is a None (driver: None, subsystem: input)
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/input/input22')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/hidraw/hidraw0') (action: "remove"). Device is a None (driver: None, subsystem: hidraw)
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001/hidraw/hidraw0')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001') (action: "unbind"). Device is a None (driver: None, subsystem: hid)
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001') (action: "remove"). Device is a None (driver: None, subsystem: hid)
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/0003:046D:C00E.0001')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0') (action: "unbind"). Device is a usb_interface (driver: None, subsystem: usb)
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0') (action: "remove"). Device is a usb_interface (driver: None, subsystem: usb)
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1') (action: "unbind"). Device is a usb_device (driver: None, subsystem: usb)
Did not account for a unbind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1') (action: "remove"). Device is a usb_device (driver: None, subsystem: usb)
Did not account for a remove action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-1')
Simple USB Keyboard (note: At this point I'd temporarily commented out filters in order to find more subsystem labels):
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3') (action: "add"). Device is a usb_device (driver: usb, subsystem: usb)
This is a USB device.
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0') (action: "add"). Device is a usb_interface (driver: usbhid, subsystem: usb)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002') (action: "add"). Device is a None (driver: hid-generic, subsystem: hid)
Unaccounted-for event (subsystem: hid, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23') (action: "add"). Device is a None (driver: None, subsystem: input)
Unaccounted-for event (subsystem: input, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::numlock') (action: "add"). Device is a None (driver: None, subsystem: leds)
Unaccounted-for event (subsystem: leds, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::capslock') (action: "add"). Device is a None (driver: None, subsystem: leds)
Unaccounted-for event (subsystem: leds, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::scrolllock') (action: "add"). Device is a None (driver: None, subsystem: leds)
Unaccounted-for event (subsystem: leds, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::numlock') (action: "change"). Device is a None (driver: None, subsystem: leds)
Did not account for a change action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::numlock')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/hidraw/hidraw0') (action: "add"). Device is a None (driver: None, subsystem: hidraw)
Unaccounted-for event (subsystem: hidraw, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::capslock') (action: "change"). Device is a None (driver: None, subsystem: leds)
Did not account for a change action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::capslock')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::scrolllock') (action: "change"). Device is a None (driver: None, subsystem: leds)
Did not account for a change action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/input23::scrolllock')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002/input/input23/event17') (action: "add"). Device is a None (driver: None, subsystem: input)
Unaccounted-for event (subsystem: input, device_type: None)
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002') (action: "bind"). Device is a None (driver: hid-generic, subsystem: hid)
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:413C:2005.0002')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0') (action: "bind"). Device is a usb_interface (driver: usbhid, subsystem: usb)
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0')
Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3') (action: "bind"). Device is a usb_device (driver: usb, subsystem: usb)
Did not account for a bind action on Device(u'/sys/devices/pci0000:00/0000:00:14.0/usb3/3-3')
Some laptop battery change. Wasn't paying attention to what caused this, it happened a while before I noticed it.
Laptop was unplugged for a decent amonut of time before the event happened. Maybe it crossed some percent threshold while discharging?
Device(u'/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/PNP0C0A:00/power_supply/BAT0') (action: "change"). Device is a None (driver: None, subsystem: power_supply)
Did not account for a change action on Device(u'/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:08/PNP0C09:00/PNP0C0A:00/power_supply/BAT0')
'''
@lucianolacurcia
Copy link

Thanks for sharing this!
I couldn't found any list of all possible values for subsystem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment