622 lines
29 KiB
Markdown
622 lines
29 KiB
Markdown
# udev
|
||
|
||
## What is udev?
|
||
|
||
Udev (userspace /dev) is a Linux sub-system for dynamic device detection and management, since kernel version 2.6. It’s a replacement of [devfs](https://wiki.debian.org/DevFS) and hotplug.
|
||
It dynamically creates or removes device nodes (an interface to a device driver that appears in a file system as if it were an ordinary file, stored under the /dev directory) at boot time or if you add a device to or remove a device from the system.
|
||
It then propagates information about a device or changes to its state to user space.
|
||
The goal of udev, as stated by the [project](https://www.linux.com/news/udev-introduction-device-management-modern-linux-system/) is:
|
||
|
||
* Run in user space.
|
||
* Create persistent device names, take the device naming out of kernel space and implement rule based device naming.
|
||
* Create a dynamic /dev with device nodes for devices present in the system and allocate major/minor numbers dynamically.
|
||
* Provide a user space API to access the device information in the system.
|
||
|
||
One of the pros of udev is that it can use persistent device names to guarantee consistent naming of devices across reboots, despite their order of discovery.
|
||
This feature is useful because the kernel simply assigns unpredictable device names based on the order of discovery.
|
||
|
||
## How does it work?
|
||
|
||
As udev runs in [userland](https://unix.stackexchange.com/questions/137820/whats-the-difference-of-the-userland-vs-the-kernel), it runs a service which on most modern distributions is controlled by [systemd](./learning_systemd.md).
|
||
If we have a look at our running processes and search for `udev` we find the following.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ ps aux | grep udev
|
||
root 430 0.0 0.0 23252 4164 ? Ss Aug06 0:01 /lib/systemd/systemd-udevd
|
||
waldek 11628 0.0 0.0 6208 896 pts/4 S+ 10:11 0:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn udev
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
The first line hints us to a process run by root with a pretty low `PID`.
|
||
It seems to be part of the `systemd` *suite* of programs.
|
||
As always, let's have a look at the `man systemd-udevd` to learn what it is and how we can interact with it.
|
||
|
||
```
|
||
DESCRIPTION
|
||
systemd-udevd listens to kernel uevents. For every event, systemd-udevd executes matching instructions
|
||
specified in udev rules. See udev(7).
|
||
|
||
The behavior of the daemon can be configured using udev.conf(5), its command line options, environment
|
||
variables, and on the kernel command line, or changed dynamically with udevadm control.
|
||
```
|
||
|
||
The description is pretty solid and points us to a specific program we can use to *talk* to the daemon.
|
||
This is very similar to how we have been talking to `systemd` via `systemctl` but here, to talk to `systemd-udevd`, we'll use `udevadm`.
|
||
|
||
## Interacting with udev
|
||
|
||
### Monitoring events
|
||
|
||
To analyse what udev is receiving we can monitor it with `sudo udevadm monitor`.
|
||
You'll see see the following output with a *blinking* cursor on the last line to show we're monitoring *live*.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ sudo udevadm monitor
|
||
monitor will print the received events for:
|
||
UDEV - the event which udev sends out after rule processing
|
||
KERNEL - the kernel uevent
|
||
```
|
||
|
||
If we now **plug in** a device we'll see a bunch of messages arriving.
|
||
He I **added** a device by plugging it into the **USB** port.
|
||
You can see a lot of *sound* and *midi* messages so it's safe to say I plugged in a MIDI keyboard.
|
||
Do notice that once the keyboard is fully recognized and initialized by the system the messages stop.
|
||
Udev has done it's work and will remain silent until some changes happen to the system.
|
||
|
||
```
|
||
KERNEL[212709.657308] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2 (usb)
|
||
KERNEL[212709.657495] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0 (usb)
|
||
KERNEL[212709.657545] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1 (sound)
|
||
KERNEL[212709.657658] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/midiC1D0 (sound)
|
||
KERNEL[212709.657725] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/midi1 (sound)
|
||
KERNEL[212709.657774] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/dmmidi1 (sound)
|
||
KERNEL[212709.657801] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/seq-midi-1-0 (snd_seq)
|
||
KERNEL[212709.657829] bind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/seq-midi-1-0 (snd_seq)
|
||
KERNEL[212709.657910] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/controlC1 (sound)
|
||
KERNEL[212709.657967] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/mixer1 (sound)
|
||
KERNEL[212709.658046] bind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0 (usb)
|
||
KERNEL[212709.658086] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1 (usb)
|
||
KERNEL[212709.658121] bind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1 (usb)
|
||
KERNEL[212709.658166] bind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2 (usb)
|
||
UDEV [212709.673568] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2 (usb)
|
||
UDEV [212709.675863] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0 (usb)
|
||
UDEV [212709.676560] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1 (usb)
|
||
UDEV [212709.677850] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1 (sound)
|
||
UDEV [212709.678004] bind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1 (usb)
|
||
UDEV [212709.690171] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/midiC1D0 (sound)
|
||
UDEV [212709.690273] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/midi1 (sound)
|
||
KERNEL[212709.692086] change /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1 (sound)
|
||
UDEV [212709.692888] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/seq-midi-1-0 (snd_seq)
|
||
UDEV [212709.693659] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/mixer1 (sound)
|
||
UDEV [212709.694897] bind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/seq-midi-1-0 (snd_seq)
|
||
UDEV [212709.696017] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/dmmidi1 (sound)
|
||
UDEV [212709.705167] add /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/controlC1 (sound)
|
||
UDEV [212709.706615] bind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0 (usb)
|
||
UDEV [212709.710665] bind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2 (usb)
|
||
UDEV [212709.712669] change /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1 (sound)
|
||
```
|
||
|
||
When I **unplug** the MIDI keyboard we get the following output.
|
||
Notice the sequence of events here.
|
||
The kernel reports changes which udev uses to **trigger** events and actions.
|
||
|
||
```
|
||
KERNEL[213014.417312] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/mixer1 (sound)
|
||
KERNEL[213014.417546] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/controlC1 (sound)
|
||
KERNEL[213014.417574] unbind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/seq-midi-1-0 (snd_seq)
|
||
KERNEL[213014.417593] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/seq-midi-1-0 (snd_seq)
|
||
KERNEL[213014.417655] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/midi1 (sound)
|
||
KERNEL[213014.417711] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/dmmidi1 (sound)
|
||
KERNEL[213014.417782] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/midiC1D0 (sound)
|
||
KERNEL[213014.417808] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1 (sound)
|
||
KERNEL[213014.417837] unbind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0 (usb)
|
||
KERNEL[213014.417870] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0 (usb)
|
||
KERNEL[213014.417904] unbind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1 (usb)
|
||
KERNEL[213014.417939] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1 (usb)
|
||
KERNEL[213014.418036] unbind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2 (usb)
|
||
KERNEL[213014.418080] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2 (usb)
|
||
UDEV [213014.419887] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/controlC1 (sound)
|
||
UDEV [213014.420067] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/mixer1 (sound)
|
||
UDEV [213014.420472] unbind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/seq-midi-1-0 (snd_seq)
|
||
UDEV [213014.421955] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/seq-midi-1-0 (snd_seq)
|
||
UDEV [213014.422602] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/midiC1D0 (sound)
|
||
UDEV [213014.422717] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/midi1 (sound)
|
||
UDEV [213014.424188] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1/dmmidi1 (sound)
|
||
UDEV [213014.424383] unbind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1 (usb)
|
||
UDEV [213014.425445] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.1 (usb)
|
||
UDEV [213014.425718] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/sound/card1 (sound)
|
||
UDEV [213014.427640] unbind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0 (usb)
|
||
UDEV [213014.430208] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0 (usb)
|
||
UDEV [213014.432609] unbind /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2 (usb)
|
||
UDEV [213014.438393] remove /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2 (usb)
|
||
```
|
||
|
||
**In virtualbox you can add or remove USB devices via the menu bar of you virtual machine.**
|
||
|
||
We can use `udevadm` for more than just monitoring.
|
||
Tab complete to the rescue!
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ sudo udevadm
|
||
control -- control the udev daemon
|
||
info -- query sysfs or the udev database
|
||
monitor -- listen to kernel and udev events
|
||
settle -- wait for the event queue to finish
|
||
test -- test an event run
|
||
test-builtin -- test a built-in command
|
||
trigger -- request events from the kernel
|
||
```
|
||
|
||
You can query the help information of a specific sub command by adding the `--help` argument.
|
||
A full manual can be read via `man udevadm`.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ sudo udevadm monitor --help
|
||
udevadm monitor [OPTIONS]
|
||
|
||
Listen to kernel and udev events.
|
||
|
||
-h --help Show this help
|
||
-V --version Show package version
|
||
-p --property Print the event properties
|
||
-k --kernel Print kernel uevents
|
||
-u --udev Print udev events
|
||
-s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem
|
||
-t --tag-match=TAG Filter events by tag
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
### Listing information
|
||
|
||
#### Via external programs
|
||
|
||
Udev can be used to query device information via the `info` subcommand but there are a few other programs that are very handy to know.
|
||
We'll focus on USB devices for now, but keep in mind that udev manages everything connected to our system.
|
||
My MIDI keyboad is **not** plugged in for now and below is the output of `lsusb`.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ lsusb
|
||
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
|
||
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
|
||
Bus 001 Device 003: ID 04f2:b217 Chicony Electronics Co., Ltd Lenovo Integrated Camera (0.3MP)
|
||
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
|
||
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
Next I plug it **in** and review the output of `lsusb`.
|
||
A device is added but it's not saying much!
|
||
This is vedor dependant and this company did not fully document it's device.
|
||
Most of the time however, you'll see a short description of the device.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ lsusb
|
||
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
|
||
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
|
||
Bus 001 Device 003: ID 04f2:b217 Chicony Electronics Co., Ltd Lenovo Integrated Camera (0.3MP)
|
||
Bus 001 Device 006: ID 2702:1110
|
||
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
|
||
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
The **hexadecimal** numbers you see listed are `VENDOR_ID:PROCUCT_ID` and can be used to digg deeper into a specific device.
|
||
For this we use the `-d VENDOR_ID:PROCUCT_ID` argument, together with `-v` for verbosity.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ lsusb -d 2702:1110 -v
|
||
|
||
Bus 001 Device 006: ID 2702:1110
|
||
Device Descriptor:
|
||
bLength 18
|
||
bDescriptorType 1
|
||
bcdUSB 1.10
|
||
bDeviceClass 0
|
||
bDeviceSubClass 0
|
||
bDeviceProtocol 0
|
||
bMaxPacketSize0 64
|
||
idVendor 0x2702
|
||
idProduct 0x1110
|
||
bcdDevice 0.01
|
||
iManufacturer 0
|
||
iProduct 0
|
||
iSerial 0
|
||
bNumConfigurations 1
|
||
Configuration Descriptor:
|
||
bLength 9
|
||
bDescriptorType 2
|
||
wTotalLength 0x0065
|
||
bNumInterfaces 2
|
||
bConfigurationValue 1
|
||
iConfiguration 0
|
||
bmAttributes 0x00
|
||
(Missing must-be-set bit!)
|
||
(Bus Powered)
|
||
MaxPower 100mA
|
||
Interface Descriptor:
|
||
bLength 9
|
||
bDescriptorType 4
|
||
bInterfaceNumber 0
|
||
bAlternateSetting 0
|
||
bNumEndpoints 0
|
||
bInterfaceClass 1 Audio
|
||
bInterfaceSubClass 1 Control Device
|
||
bInterfaceProtocol 0
|
||
iInterface 0
|
||
AudioControl Interface Descriptor:
|
||
bLength 9
|
||
bDescriptorType 36
|
||
bDescriptorSubtype 1 (HEADER)
|
||
bcdADC 1.00
|
||
wTotalLength 0x0009
|
||
bInCollection 1
|
||
baInterfaceNr(0) 1
|
||
Interface Descriptor:
|
||
bLength 9
|
||
bDescriptorType 4
|
||
bInterfaceNumber 1
|
||
bAlternateSetting 0
|
||
bNumEndpoints 2
|
||
bInterfaceClass 1 Audio
|
||
bInterfaceSubClass 3 MIDI Streaming
|
||
bInterfaceProtocol 0
|
||
iInterface 0
|
||
MIDIStreaming Interface Descriptor:
|
||
bLength 7
|
||
bDescriptorType 36
|
||
bDescriptorSubtype 1 (HEADER)
|
||
bcdADC 1.00
|
||
wTotalLength 0x0041
|
||
MIDIStreaming Interface Descriptor:
|
||
bLength 6
|
||
bDescriptorType 36
|
||
bDescriptorSubtype 2 (MIDI_IN_JACK)
|
||
bJackType 1 Embedded
|
||
bJackID 1
|
||
iJack 0
|
||
MIDIStreaming Interface Descriptor:
|
||
bLength 6
|
||
bDescriptorType 36
|
||
bDescriptorSubtype 2 (MIDI_IN_JACK)
|
||
bJackType 2 External
|
||
bJackID 2
|
||
iJack 0
|
||
MIDIStreaming Interface Descriptor:
|
||
bLength 9
|
||
bDescriptorType 36
|
||
bDescriptorSubtype 3 (MIDI_OUT_JACK)
|
||
bJackType 1 Embedded
|
||
bJackID 3
|
||
bNrInputPins 1
|
||
baSourceID( 0) 2
|
||
BaSourcePin( 0) 1
|
||
iJack 0
|
||
MIDIStreaming Interface Descriptor:
|
||
bLength 9
|
||
bDescriptorType 36
|
||
bDescriptorSubtype 3 (MIDI_OUT_JACK)
|
||
bJackType 2 External
|
||
bJackID 4
|
||
bNrInputPins 1
|
||
baSourceID( 0) 1
|
||
BaSourcePin( 0) 1
|
||
iJack 0
|
||
Endpoint Descriptor:
|
||
bLength 9
|
||
bDescriptorType 5
|
||
bEndpointAddress 0x02 EP 2 OUT
|
||
bmAttributes 2
|
||
Transfer Type Bulk
|
||
Synch Type None
|
||
Usage Type Data
|
||
wMaxPacketSize 0x0040 1x 64 bytes
|
||
bInterval 0
|
||
bRefresh 0
|
||
bSynchAddress 0
|
||
MIDIStreaming Endpoint Descriptor:
|
||
bLength 5
|
||
bDescriptorType 37
|
||
bDescriptorSubtype 1 (GENERAL)
|
||
bNumEmbMIDIJack 1
|
||
baAssocJackID( 0) 1
|
||
Endpoint Descriptor:
|
||
bLength 9
|
||
bDescriptorType 5
|
||
bEndpointAddress 0x81 EP 1 IN
|
||
bmAttributes 2
|
||
Transfer Type Bulk
|
||
Synch Type None
|
||
Usage Type Data
|
||
wMaxPacketSize 0x0040 1x 64 bytes
|
||
bInterval 0
|
||
bRefresh 0
|
||
bSynchAddress 0
|
||
MIDIStreaming Endpoint Descriptor:
|
||
bLength 5
|
||
bDescriptorType 37
|
||
bDescriptorSubtype 1 (GENERAL)
|
||
bNumEmbMIDIJack 1
|
||
baAssocJackID( 0) 3
|
||
can't get debug descriptor: Resource temporarily unavailable
|
||
Device Status: 0x0000
|
||
(Bus Powered)
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
A USB stick presents itself as follows and offers quite a bit more information than our generic MIDI keyboard.
|
||
We get a unique serial number, speeds, power usage and more.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ lsusb -d 0781:5591 -v
|
||
|
||
Bus 001 Device 007: ID 0781:5591 SanDisk Corp. Ultra Flair
|
||
Device Descriptor:
|
||
bLength 18
|
||
bDescriptorType 1
|
||
bcdUSB 2.10
|
||
bDeviceClass 0
|
||
bDeviceSubClass 0
|
||
bDeviceProtocol 0
|
||
bMaxPacketSize0 64
|
||
idVendor 0x0781 SanDisk Corp.
|
||
idProduct 0x5591 Ultra Flair
|
||
bcdDevice 1.00
|
||
iManufacturer 1 USB
|
||
iProduct 2 SanDisk 3.2Gen1
|
||
iSerial 3 0101afb5176b84241e77e68fb386ea23b31fe4cf9eafe43b465def85b4531ad93da300000000000000000000014b07c6ff8d260091558107b52921c2
|
||
bNumConfigurations 1
|
||
Configuration Descriptor:
|
||
bLength 9
|
||
bDescriptorType 2
|
||
wTotalLength 0x0020
|
||
bNumInterfaces 1
|
||
bConfigurationValue 1
|
||
iConfiguration 0
|
||
bmAttributes 0x80
|
||
(Bus Powered)
|
||
MaxPower 224mA
|
||
Interface Descriptor:
|
||
bLength 9
|
||
bDescriptorType 4
|
||
bInterfaceNumber 0
|
||
bAlternateSetting 0
|
||
bNumEndpoints 2
|
||
bInterfaceClass 8 Mass Storage
|
||
bInterfaceSubClass 6 SCSI
|
||
bInterfaceProtocol 80 Bulk-Only
|
||
iInterface 0
|
||
Endpoint Descriptor:
|
||
bLength 7
|
||
bDescriptorType 5
|
||
bEndpointAddress 0x81 EP 1 IN
|
||
bmAttributes 2
|
||
Transfer Type Bulk
|
||
Synch Type None
|
||
Usage Type Data
|
||
wMaxPacketSize 0x0200 1x 512 bytes
|
||
bInterval 0
|
||
Endpoint Descriptor:
|
||
bLength 7
|
||
bDescriptorType 5
|
||
bEndpointAddress 0x02 EP 2 OUT
|
||
bmAttributes 2
|
||
Transfer Type Bulk
|
||
Synch Type None
|
||
Usage Type Data
|
||
wMaxPacketSize 0x0200 1x 512 bytes
|
||
bInterval 0
|
||
Binary Object Store Descriptor:
|
||
bLength 5
|
||
bDescriptorType 15
|
||
wTotalLength 0x0016
|
||
bNumDeviceCaps 2
|
||
USB 2.0 Extension Device Capability:
|
||
bLength 7
|
||
bDescriptorType 16
|
||
bDevCapabilityType 2
|
||
bmAttributes 0x00000002
|
||
HIRD Link Power Management (LPM) Supported
|
||
SuperSpeed USB Device Capability:
|
||
bLength 10
|
||
bDescriptorType 16
|
||
bDevCapabilityType 3
|
||
bmAttributes 0x00
|
||
wSpeedsSupported 0x000e
|
||
Device can operate at Full Speed (12Mbps)
|
||
Device can operate at High Speed (480Mbps)
|
||
Device can operate at SuperSpeed (5Gbps)
|
||
bFunctionalitySupport 1
|
||
Lowest fully-functional device speed is Full Speed (12Mbps)
|
||
bU1DevExitLat 10 micro seconds
|
||
bU2DevExitLat 256 micro seconds
|
||
can't get debug descriptor: Resource temporarily unavailable
|
||
Device Status: 0x0000
|
||
(Bus Powered)
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
#### Via udevadm
|
||
|
||
We can get the same information via `udevadm` with the `info` subcommand.
|
||
We'll have to specify *which* device we want to query and this can be done via different identifiers.
|
||
The USB stick I inserted is still plugged in and represented in my filesystem via `/dev/sdc`.
|
||
Remember TAB complete!
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ sudo udevadm info --path /sys/block/sdc
|
||
[sudo] password for waldek:
|
||
P: /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host6/target6:0:0/6:0:0:0/block/sdc
|
||
N: sdc
|
||
L: 0
|
||
S: disk/by-id/usb-USB_SanDisk_3.2Gen1_0101afb5176b84241e77e68fb386ea23b31fe4cf9eafe43b465def85b4531ad93da300000000000000000000014b07c6ff8d260091558107b52921c2-0:0
|
||
S: disk/by-path/pci-0000:00:1a.0-usb-0:1.2:1.0-scsi-0:0:0:0
|
||
E: DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host6/target6:0:0/6:0:0:0/block/sdc
|
||
E: DEVNAME=/dev/sdc
|
||
E: DEVTYPE=disk
|
||
E: MAJOR=8
|
||
E: MINOR=32
|
||
E: SUBSYSTEM=block
|
||
E: USEC_INITIALIZED=216426658045
|
||
E: ID_VENDOR=USB
|
||
E: ID_VENDOR_ENC=\x20USB\x20\x20\x20\x20
|
||
E: ID_VENDOR_ID=0781
|
||
E: ID_MODEL=SanDisk_3.2Gen1
|
||
E: ID_MODEL_ENC=\x20SanDisk\x203.2Gen1
|
||
E: ID_MODEL_ID=5591
|
||
E: ID_REVISION=1.00
|
||
E: ID_SERIAL=USB_SanDisk_3.2Gen1_0101afb5176b84241e77e68fb386ea23b31fe4cf9eafe43b465def85b4531ad93da300000000000000000000014b07c6ff8d260091558107b52921c2-0:0
|
||
E: ID_SERIAL_SHORT=0101afb5176b84241e77e68fb386ea23b31fe4cf9eafe43b465def85b4531ad93da300000000000000000000014b07c6ff8d260091558107b52921c2
|
||
E: ID_TYPE=disk
|
||
E: ID_INSTANCE=0:0
|
||
E: ID_BUS=usb
|
||
E: ID_USB_INTERFACES=:080650:
|
||
E: ID_USB_INTERFACE_NUM=00
|
||
E: ID_USB_DRIVER=usb-storage
|
||
E: ID_PATH=pci-0000:00:1a.0-usb-0:1.2:1.0-scsi-0:0:0:0
|
||
E: ID_PATH_TAG=pci-0000_00_1a_0-usb-0_1_2_1_0-scsi-0_0_0_0
|
||
E: ID_PART_TABLE_UUID=2ab572a6-8db9-4324-9d21-1a3640f4fb19
|
||
E: ID_PART_TABLE_TYPE=gpt
|
||
E: DEVLINKS=/dev/disk/by-id/usb-USB_SanDisk_3.2Gen1_0101afb5176b84241e77e68fb386ea23b31fe4cf9eafe43b465def85b4531ad93da300000000000000000000014b07c6ff8d260091558107b52921c2-0:0 /dev/disk/by-path/pci-0000:00:1a.0-usb-0:1.2:1.0-scsi-0:0:0:0
|
||
E: TAGS=:systemd:
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
We can also inspect information about the **partions** on the disk, even without **mounting** them.
|
||
I'll spare you the output of the command but you would do `sudo udevadm info --name=/dev/sdc1` wich gives us the label, type of format, etc.
|
||
|
||
### Using the information
|
||
|
||
It's nice to *know* more about a device but *doing* something useful is even better.
|
||
This is the real beauty of `udev`.
|
||
We can use the device information to trigger different actions when a device is added, removed or modified.
|
||
This is done by adding **rules** which can be defined in `/etc/udev/rules.d/`.
|
||
Let's have a look at what's present on one of my servers.
|
||
|
||
```
|
||
➜ ~ ls -l /etc/udev/rules.d/
|
||
total 4
|
||
-rw-r--r-- 1 root root 96 Mar 4 12:31 70-persistent-net.rules
|
||
➜ ~ cat /etc/udev/rules.d/70-persistent-net.rules
|
||
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="fa:16:3e:9a:9f:3f", NAME="eth0"
|
||
➜ ~
|
||
```
|
||
|
||
This is a rule that fixes the network card with a specific mac address to a fixed name, eth0, each time this device is added.
|
||
A complete description of what can be used to form rules and actions can be read in the `man udev` pages.
|
||
|
||
#### A simple rule
|
||
|
||
Let's add a rule so that whenever I plug in a specific USB stick, it always has a fixed path in the `/dev` tree.
|
||
For this I need find a **unique** value of this USB drive so that my rule will only appy to this specific drive.
|
||
I can find the serial number for the first partition by executing `sudo udevadm info --path=/sys/block/sdc -a | grep -E --color=auto 'serial|$'`.
|
||
The `grep` command is a handy way of highlighting one specific word (do you understand what the `|$` means?).
|
||
Based on the output from this command I can construct the following rule.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ cat /etc/udev/rules.d/classes.rules
|
||
ACTION=="add", ATTRS{serial}=="0101afb5176b84241e77e68fb386ea23b31fe4cf9eafe43b465def85b4531ad93da300000000000000000000014b07c6ff8d260091558107b52921c2", SYMLINK+="sparedisk"
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
Now that the rule has been created we need to tell `udev` to take this new rule into account.
|
||
This is very similar to how `systemd` needs a `systemctl daemon-reload` when we make changes to service files.
|
||
This is done with the following command.
|
||
Once this is done I can plug the drive out and in I'll see my `/dev/sparedisk` apprear!
|
||
|
||
```
|
||
sudo udevadm control --reload
|
||
➜ ~ git:(master) ✗ ls /dev/sparedisk -l
|
||
ls: cannot access '/dev/sparedisk': No such file or directory
|
||
➜ ~ git:(master) ✗ ls /dev/sparedisk -l
|
||
lrwxrwxrwx 1 root root 4 Aug 20 13:29 /dev/sparedisk -> sdc1
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
#### A more practical rule
|
||
|
||
There is a lot more we can do with rules so let's create a more complicated one.
|
||
I could like to log every block device connected to the system to a specific file for security monitoring.
|
||
For this I'll write a simple `bash` script and a `udev` rule that should trigger at each **add** of a **block** device.
|
||
Let's do the rule first.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ cat /etc/udev/rules.d/classes.rules
|
||
SUBSYSTEM=="block", ACTION=="add", RUN="/home/waldek/bin/block_monitor.sh"
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
The script is just a placeholder for now to prove the script get's triggered.
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ cat bin/block_monitor.sh
|
||
#!/bin/bash
|
||
|
||
echo "helloworld: $(date)" >> /tmp/block_monitor.log
|
||
➜ ~ git:(master) ✗
|
||
|
||
```
|
||
|
||
And after plugging the USB stick in **2** times I get the following output.
|
||
Does this look normal to you?
|
||
Why are we getting two entries per plug in?
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ cat /tmp/block_monitor.log
|
||
helloworld: Fri Aug 20 12:35:42 BST 2021
|
||
helloworld: Fri Aug 20 12:35:43 BST 2021
|
||
helloworld: Fri Aug 20 12:36:04 BST 2021
|
||
helloworld: Fri Aug 20 12:36:04 BST 2021
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
OK, the proof of concept is working but let's make it an actual useful log.
|
||
There is a very nice feature hidden in the `man udev` pages towards the end.
|
||
I'll outline the base but please go have a look to see what more you can do with it.
|
||
|
||
```
|
||
The NAME, SYMLINK, PROGRAM, OWNER, GROUP, MODE, SECLABEL, and RUN
|
||
fields support simple string substitutions. The RUN substitutions
|
||
are performed after all rules have been processed, right before
|
||
the program is executed, allowing for the use of device
|
||
properties set by earlier matching rules. For all other fields,
|
||
substitutions are performed while the individual rule is being
|
||
processed. The available substitutions are:
|
||
```
|
||
|
||
With this in mind we can pass more information on to our script.
|
||
I'll start by using the `%p` and `$name` arguments to the RUN call.
|
||
The rule now looks as follows (and I reload the rules to take the changes into account).
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ cat /etc/udev/rules.d/classes.rules
|
||
SUBSYSTEM=="block", ACTION=="add", RUN="/home/waldek/bin/block_monitor.sh %p $name"
|
||
➜ ~ git:(master) ✗ sudo udevadm control --reload
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
I'll have to modify the script a bit to write these changes to my log file as well.
|
||
Remember the `$1` and `$2` meaning in `bash`?
|
||
|
||
```
|
||
➜ ~ git:(master) ✗ cat bin/block_monitor.sh
|
||
#!/bin/bash
|
||
|
||
echo "helloworld: $(date) - $1 - $2" >> /tmp/block_monitor.log
|
||
➜ ~ git:(master) ✗ tail -n 4 /tmp/block_monitor.log
|
||
helloworld: Fri Aug 20 12:46:29 BST 2021 - /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host6/target6:0:0/6:0:0:0/block/sdc - sdc
|
||
helloworld: Fri Aug 20 12:46:29 BST 2021 - /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host6/target6:0:0/6:0:0:0/block/sdc/sdc1 - sdc1
|
||
helloworld: Fri Aug 20 12:50:33 BST 2021 - /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host6/target6:0:0/6:0:0:0/block/sdc - sdc
|
||
helloworld: Fri Aug 20 12:50:33 BST 2021 - /devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2:1.0/host6/target6:0:0/6:0:0:0/block/sdc/sdc1 - sdc1
|
||
➜ ~ git:(master) ✗
|
||
```
|
||
|
||
As the rule triggers the script, we can make changes to this script without reloading the rules.
|
||
I advise to de-couple the development of the rule from the development of the script.
|
||
First make sure your rule works as expected with some sort of script placeholder you can verify, next develop your script and make it as robust as possible.
|
||
|
||
|
||
## Conclusion
|
||
|
||
This is just an introduction the fundamental concepts and usage of `udev`.
|
||
We'll tie it together with `systemd` once we've investigated [dbus](./learning_dbus.md).
|