diff --git a/modules/qualifying/learning_udev.md b/modules/qualifying/learning_udev.md index fa5a88a..26717a9 100644 --- a/modules/qualifying/learning_udev.md +++ b/modules/qualifying/learning_udev.md @@ -484,4 +484,128 @@ E: TAGS=:systemd: 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) ✗ +```