© David Both 2020
David BothUsing and Administering Linux: Volume 2https://doi.org/10.1007/978-1-4842-5455-4_14

14. D-Bus and udev

David Both
(1)
Raleigh, NC, USA
 

Objectives

In this chapter you will learn
  • How Linux treats all devices as plug and play
  • What D-Bus and udev are
  • How D-Bus and udev work together to make device access easy
  • How to write rules for udev

/dev chaos

The /dev directory has always been the location for the device files in all Unix and Linux operating systems. Note that device files are not the same as device drivers. Each device file represents one actual or potential physical device connected to the host.
In the past, device files were created at the time the operating system was created. This meant that all possible devices that might ever be used on a system needed to be created in advance. In fact, tens of thousands of device files needed to be created to handle all of the possibilities. It became very difficult to determine which device file actually related to a specific physical device.
The development of two very important tools, D-Bus and udev, has provided Linux with the ability to create device files only when they are needed by a device that is already installed or one that is hot-plugged into the running system.

About D-Bus

D-Bus1 is a Linux software interface used for inter-process communications (IPC). It was first released in 2006. We looked at one form of IPC in Volume 1, Chapter 13, the named pipe, in which one program would push data into the named pipe and another program would extract the data.
D-Bus is a system-wide and more complex form of IPC that allows many kernel- and system-level processes to send messages to the logical message bus. Other processes listen to the messages on the bus and may choose to react to those messages or not.

About udev

The udev2 daemon is designed to simplify the chaos that had overtaken the /dev directory with huge numbers of mostly unneeded devices. At startup, udev creates entries in /dev only for those devices that actually currently exist or which have a high probability of actually existing on the host. This significantly reduces the number of device files required.
In addition to detecting devices, udev assigns names to those devices when they are plugged into the system, such as USB storage and printers, and other non-USB types of devices as well. In fact, udev treats all devices as plug and play, even at boot time. This makes dealing with devices consistent at all times. udev also moves device naming out of kernel space and into user space.
Greg Kroah-Hartman, one of the authors of udev, wrote an interesting and informative article3 for Linux Journal. It provides insight into the details of udev and how it is supposed to work. That article discusses udev, a program that replaces and improves upon the functionality of the old devfs. It provides /dev entries for devices in the system at any moment in time. It also provides features previously unavailable through devfs alone, such as persistent naming for devices when they move around the device tree, a flexible device naming scheme, notification of external systems of device changes, and moving all naming policy out of the kernel.
Note that udev has matured since the article was written and some things have changed, such as the udev rule location and structure, but the overall objectives and architecture are the same.
One of the main consequences of using udev for persistent plug'n'play naming is that it makes things much easier for the average non-technical user. This is a good thing in the long run; however, there have been migration problems.
Experiment 14-1
Perform this experiment as the student user on the GUI desktop. This experiment assumes that the USB stick is formatted and has one partition.
Open the Thunar file manager and ensure that the side panel is visible. It does not matter whether it is in Shortcuts or Tree mode because the storage devices are visible in both.
Plug a USB thumb drive that is known to be working into the physical host. Then, on the VirtualBox window for StudentVM1, use the menu bar to open Devices ➤ USB and, while watching the Thunar window, place a check mark next to the USB device you just plugged in. The device will now be available to the VM, and it will be shown in the Thunar side panel.
Verify that the new device special file has been created in /dev/.
[root@studentvm1 ~]# ll /dev | grep sd
brw-rw----  1 root    disk      8,   0 May 17 11:35 sda
brw-rw----  1 root    disk      8,   1 May 17 11:35 sda1
brw-rw----  1 root    disk      8,   2 May 17 11:35 sda2
brw-rw----  1 root    disk      8,  16 May 17 11:35 sdb
brw-rw----  1 root    disk      8,  17 May 17 11:35 sdb1
brw-rw----  1 root    disk      8,  18 May 17 11:35 sdb2
brw-rw----  1 root    disk      8,  32 May 17 11:35 sdc
brw-rw----  1 root    disk      8,  48 May 17 11:35 sdd
brw-rw----  1 root    disk      8,  64 May 20 08:29 sde
brw-rw----  1 root    disk      8,  65 May 20 08:29 sde1
You should see that the new devices, /dev/sde and /dev/sde1, have been created within the last couple minutes or so. One device special file was created for the entire device, /dev/sde, and one was created for the partition on the device, /dev/sde1. The device name may be different on your VM depending upon how closely you have been performing the experiments in the preceding chapters of this course.
The D-Bus and udev services work together to make this happen.
Here is a simplified version of what takes place when a new device is connected to the host. I stipulate here that the host system is already booted and running at multi-user.target (runlevel 3) or graphical.target (runlevel 5):
  1. 1.
    The user plugs in a new device, usually into an external USB, SATA, or eSATA connector.
     
  2. 2.
    The kernel detects this and sends a message on D-Bus to announce the new device.
     
  3. 3.
    udev reads the message proffered on D-Bus.
     
  4. 4.
    Based on the device properties and its location in the hardware bus tree, udev creates a name for the new device if one does not already exist.
     
  5. 5.
    The udev system creates the device special file in /dev.
     
  6. 6.
    If a new device driver is required, it is loaded.
     
  7. 7.
    The device is initialized.
     
  8. 8.
    udev may send a notification to the desktop so that the desktop may display an icon for the new device to the user.
     
The process of hot-plugging a new hardware device into a running Linux system and making it ready is very complex – for the operating system. It is very simple for the user who just wants to plug in a new device and have it work. This simplifies things immensely for the end user. For USB and SATA hard drives, USB thumb drives, keyboards, mice, printers, displays, and nearly anything else, all that a user needs to do is to plug the device into the appropriate USB or SATA port and it will work.

Naming rules

udev stores its default naming rules in files in the /usr/lib/udev/rules.d directory, and it’s local configuration files in the /etc/udev/rules.d directory. Each file contains a set of rules for a specific device type. These rules should not be changed.
In earlier versions of udev, there were many local rule sets created, including a set for NIC naming. As each NIC was discovered by the kernel and renamed by udev for the very first time, a rule was added to the rule set for the network device type. This was initially done to ensure consistency before names had changed from “ethX” to more consistent ones.
Now that udev has multiple consistent default rules for determining device names, especially for NICs, storing the specific rules for each device in local configuration files is not required to maintain that consistency.
Experiment 14-2
Perform this experiment as the student user. We can look at the startup log for our VM and see where the NIC names were changed. You may also encounter some other messages that match the search pattern.
[student@studentvm1 ~]$ dmesg | grep -i eth
[    5.014594] e1000 0000:00:03.0 eth0: (PCI:33MHz:32-bit) 08:00:27:e1:0c:10
[    5.014643] e1000 0000:00:03.0 eth0: Intel(R) PRO/1000 Network Connection
[    5.592516] e1000 0000:00:08.0 eth1: (PCI:33MHz:32-bit) 08:00:27:f6:b0:68
[    5.592559] e1000 0000:00:08.0 eth1: Intel(R) PRO/1000 Network Connection
[    5.595377] e1000 0000:00:03.0 enp0s3: renamed from eth0
[    5.614190] e1000 0000:00:08.0 enp0s8: renamed from eth1
This result is from my VM which has two NICs so there are lines for both. You should have only a single NIC so only one set of entries in the dmesg data stream.
The first line for eth0 shows when the NIC was “discovered,” at 5.014594 seconds after startup. The second line shows the ID of the device as an (virtual) Intel network device. The last line for eth0 records the renaming from eth0 to enp0s3.

Making udev work

This section first appeared as an article4 by Seth Kenlon at Opensource.com. It was published there under a CC-by-SA 45 license and is used here with permission of the author. I thought about writing this section myself, but this is such a well-written piece and it covers everything I wanted to do myself, so I decided to use it here. The only changes I have made are to remove the use of sudo and to better incorporate it into the format of this book.
This is an excellent example of authors who share their work using a Creative Commons license I did not legally need to ask permission because the license does not require that, but I felt that was the appropriate thing to do.
§§

Using Udev for your success

Udev is the subsystem in Linux that supplies your computer with device events. In plain English, that means it’s the code that detects when you have things plugged into your computer, like a network card, external hard drives (including USB thumb drives), mice, keyboards, joysticks and gamepads, DVD-ROM drives, and so on. That makes it a potentially useful utility, and it’s well enough exposed to a standard user such that you can manually script it to, for instance, perform certain tasks when a certain hard drive is plugged in.
This article teaches you how to create a udev script triggered by some udev event, such as plugging in a specific thumb drive. Once you understand the process for working with udev, you can use it to do all manner of things, like loading a specific driver when a gamepad is attached or performing an automatic backup when your backup drive is attached.

A basic script

The best way to work with udev is in small chunks. Don’t write the entire script up front, but instead start with something that simply confirms that udev does indeed trigger some custom event.
Depending on the ultimate goal of your script, you won’t be able to guarantee that you will ever see the results of a script with your own eyes, so make your script log that it was successfully triggered. The usual place for log files is in the /var directory, but that’s mostly the root user’s domain, so for testing, use /tmp, which is accessible by normal users and also usually gets cleaned out every so often.
Open your favorite text editor as root and enter this simple script.
#!/usr/bin/bash
/usr/bin/date >> /tmp/udev.log
Place this in /usr/local/bin or some such place in the default executable path. Call it trigger.sh and, of course, make it executable with chmod +x.
[root@studentvm1 bin]# chmod +x /usr/local/bin/trigger.sh
This script has nothing to do with udev. When this script is executed, this script places a timestamp in the file /tmp/udev.log. Test the script yourself.
[root@studentvm1 ~]# trigger.sh ; cat /tmp/udev.log
Mon May 20 17:03:25 EDT 2019
The next step is to make udev, rather than yourself, trigger the script.

Unique device identification

In order for your script to be triggered by a device event, udev must know under what conditions it should call the script. In real life, you can identify a thumb drive by its color, the manufacturer, and the fact that you just plugged it into your computer. Your computer, however, obviously needs a different set of criteria.
Udev identifies devices by serial numbers, manufacturers, and even vendor ID and product ID numbers. Since this is early in the life span of your udev script, be as broad, non-specific, and all-inclusive as possible. In other words, you want first to catch nearly any valid udev event to trigger your script.
With the udevadm monitor command, you can tap into udev in real time and see what it sees when you plug in different devices. Try it as root.
[root@studentvm1 ~]# udevadm monitor
The monitor function prints received events for
  • UDEV: The event which udev sends out after rule processing
  • KERNEL: The kernel uevent
With udevadm monitor running, plug in a thumb drive and watch as all kinds of information are spewed out onto your screen. Notice, particularly, that the type of event is an ADD event. That’s a good way of identifying what type of event you want.
The udevadm monitor command provides a lot of good info, but you can see it with prettier formatting with the command udevadm info, assuming you know where your thumb drive is currently located in your /dev tree. If not, unplug and then plug your thumb drive back in and then immediately issue this command.
[root@studentvm1 ~]# dmesg | tail | grep -i sd∗
[265211.509658] scsi host7: usb-storage 1-1:1.0
[265212.528687] scsi 7:0:0:0: Direct-Access     JetFlash TS512MJF150      8.07 PQ: 0 ANSI: 2
[265212.529157] sd 7:0:0:0: Attached scsi generic sg5 type 0
[265212.550431] sd 7:0:0:0: [sde] 1003520 512-byte logical blocks: (514 MB/490 MiB)
[265212.556999] sd 7:0:0:0: [sde] Write Protect is off
[265212.557006] sd 7:0:0:0: [sde] Mode Sense: 03 00 00 00
[265212.563576] sd 7:0:0:0: [sde] No Caching mode page found
[265212.563582] sd 7:0:0:0: [sde] Assuming drive cache: write through
[265212.610157]  sde: sde1
[265212.647708] sd 7:0:0:0: [sde] Attached SCSI removable disk
Assuming that command returned sde: sde1, for instance, then your thumb drive is being assigned the sde label by the kernel. Alternately, you can use the lsblk command to see all drives, including sizes and partitions, attached to your system. Now that you have established where your drive is currently located in your filesystem, you can view udev information about that device.
[root@studentvm1 ~]# udevadm info -a -n /dev/sde | less
This returns a lot of information. Focus on the first block of info for now. Your job is to pick out parts of udev’s report about a device that are most unique to that device and then tell udev to trigger your script when those unique attributes are detected.
What’s happening on a technical level is that the udevadm info process reports on a device (specified by the device path) and then “walks” up the chain of parent devices. For every device found, it prints all possible attributes using a key-value format. You can compose a rule to match according to the attributes of a device plus attributes from one single parent device.
  looking at device '/devices/pci0000:00/0000:00:0b.0/usb1/1-1/1-1:1.0/host7/target7:0:0/7:0:0:0/block/sde':
    KERNEL=="sde"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{alignment_offset}=="0"
    ATTR{capability}=="51"
    ATTR{discard_alignment}=="0"
    ATTR{events}=="media_change"
    ATTR{events_async}==""
    ATTR{events_poll_msecs}=="-1"
    ATTR{ext_range}=="256"
    ATTR{hidden}=="0"
    ATTR{inflight}=="       0        0"
    ATTR{range}=="16"
    ATTR{removable}=="1"
    ATTR{ro}=="0"
    ATTR{size}=="1003520"
    ATTR{stat}=="     109     0     4184     1434     0     0     0        0     0      116     1369     0     0     0     0
A udev rule must contain one attribute from one single parent device.
Parent attributes are things that describe a device from the most basic level, such as it’s something that has been plugged into a physical port or it is something with a size or this is a removable device. Since the KERNEL label of sde can change depending upon how many other drives you happen to have plugged in before you plug that thumb drive in, that’s not the optimal parent attribute for a udev rule. However, it works for a proof of concept, so you could use it. An even better candidate is the SUBSYSTEM attribute, which identifies that this is a “block” system device (which is why the lsblk command lists the device).
Create a new file called 80-local.rules in /etc/udev/rules.d and enter this code.
SUBSYSTEM=="block", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"
Save the file, unplug your test thumb drive, and then reboot.
Wait, reboot on a Linux machine?
Theoretically, you can just issue udevadm control --reload, which should load all rules, but at this stage in the game, it’s best to eliminate all variables. Udev is complex enough without wondering if that rule didn’t work because of a syntax error or if you just should have rebooted. So reboot regardless of what your POSIX pride tells you.
Tip
Although rebooting your StudentVM1 host at this juncture is still a very good idea, I did try using the udevadm control --reload command and the trigger.sh script did trigger as expected, leaving a new entry in /tmp/udev.log.
When your system is back online, switch to a text console (with Ctrl-Alt-F3 or similar) and plug your thumb drive in. If you are running a recent kernel, you will probably see a bunch of output in your console when you plug the drive in. If you see an error message such as “Could not execute /usr/local/bin/trigger.sh,” then you probably forgot to make the script executable. Otherwise, hopefully all you see is that a device was plugged in and that it got some kind of kernel device assignment and so on. Now, the moment of truth.
[root@studentvm1 rules.d]# cat /tmp/udev.log
Mon May 20 17:03:25 EDT 2019
Tue May 21 07:58:30 EDT 2019
If you see a very recent date and time returned from /tmp/udev.log, then the udev has successfully triggered your script.

Refining the rule into something useful

The problem with the rule right now is that it’s very generic. Plugging in a mouse, a thumb drive, or someone else’s thumb drive will all indiscriminately trigger your script. Now is the time to start focusing in on the exact thumb drive you want to trigger your script.
One way to do this is with the vendor ID and product ID. To get these numbers, you can use the lsusb command.
[root@studentvm1 rules.d]# lsusb
Bus 001 Device 006: ID 058f:6387 Alcor Micro Corp. Flash Drive
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
In this example, the 058f:6387 before “Alcor Micro Corp. Flash Drive” denotes the idVendor and idProduct attributes. You can now include these attributes in your rule. Be sure to use the IDs for your device and not the ones for my device.
SUBSYSTEM=="block", ATTRS{idVendor}=="058f", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"
Test this (yes, you should still reboot, just to make sure you’re getting fresh reactions from udev), and it should work the same as before, only now if you plug in, say, a thumb drive manufactured by a different company (therefore with a different idVendor) or a mouse or a printer, the script is not triggered.
Keep adding in new attributes to further focus in on that one unique thumb drive that you actually want to have trigger your script. Using udevadm info -a -n /dev/sde, you can find out things like the vendor name, or sometimes a serial number, or the product name, and so on.
For your own sanity, be sure to add only one new attribute at a time. Most mistakes I have made and have seen other people make are to throw a bunch of attributes into their udev rule and wonder why the thing no longer works. Testing attributes one by one is the safest way to ensure udev can identify your device successfully.

Security

This brings up the security concerns of writing udev rules to automatically do something when a drive is plugged in.
On my machines, I don’t even have automount turned on, and yet this article proposed scripts and rules that execute commands just by having something plugged in.
Two things to bear in mind here.
Focus your udev rules once you have them working so that they only trigger scripts when you really want them to. Executing a script that blindly copies data to or from your computer is a bad idea if anyone who happens to be carrying a thumb drive of the same brand as yours comes along and plugs it into your box. Do not write your udev rule and scripts and then forget about them. I know which computers have my udev rules on them, and those boxes are much more my personal computers than the ones that I take around to conferences or have in my office at work. The more “social” a computer is, the less likely it is to get a udev rule on it that could potentially result in my data ending up on someone else’s device or someone else’s data or malware on my device.
In other words, as with so much of the power that a GNU system provides you, it is your job to be mindful of how you are wielding that power. If you abuse it or fail to treat it with respect, then it very well could go horribly wrong.

Udev in the real world

Now that you can confirm that our script is triggered by udev, you can turn your attention to the function of the script. Right now, it is useless, doing nothing more than logging the fact that it has been executed. I use udev to trigger automated backups of my thumb drives. The idea is that the master copies of my active documents are on my thumb drive (since it goes everywhere I go and could be worked on at any moment), and those master documents get backed up to my computer each time I plug the drive into that machine. In other words, my computer is the backup drive and my production data is mobile.
Since that’s what I use udev for the most, it’s the example I’ll use here, but udev can grab lots of other things, like gamepads (this is useful on systems that aren’t set to load the xboxdrv module when a gamepad is attached) and cameras and microphones (useful to set inputs when a specific mic is attached), so don’t think that this one example is all it’s good for.
A simple version of my backup system is a two-command process.
SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", SYMLINK+="safety%n"
SUBSYSTEM=="block", ATTRS{idVendor}=="03f0", ACTION=="add", RUN+="/usr/local/bin/trigger.sh"
The first line detects my thumb drive with the attributes already discussed and then assigns the thumb drive a symlink within the device tree. The symlink it assigns is safety%n. The %n is a udev macro that resolves to whatever number the kernel gives to the device, such as sdb1, sdb2, sdb3, and so on. So %n would be the 1 or the 2 or the 3.
This creates only a symlink in the dev tree, so it does not interfere with the normal process of plugging in a device. This means that if you do use a desktop environment that likes to automount devices, you won’t be causing problems for it. The second line runs the script.
My backup script looks like this.
#!/usr/bin/bash
 mount /dev/safety1 /mnt/hd
sleep 2
rsync -az /mnt/hd/ /home/seth/backups/ && umount /dev/safety1
The script uses the symlink, which avoids the possibility of udev naming the drive something unexpected (for instance, if I have a thumb drive called DISK plugged into my computer already, and I plug in my other thumb drive also called DISK, the second one will be labelled DISK_, which would foil my script). It mounts safety1 (the first partition of the drive) at my preferred mount point of /mnt/hd.
Once safely mounted, it uses rsync to back up the drive to my backup folder (my actual script uses rdiff-backup, and yours can use whatever automated backup solution you prefer).

Udev is your Dev

Udev is a very flexible system and enables you to define rules and functions in ways that few other systems dare provide users. Learn it and use it, and enjoy the power of POSIX.
§§

Chapter summary

In this chapter we have explored how D-Bus and udev work together to enable a very powerful and flexible plug and play feature for Linux. We have looked at how udev works to provide names for newly plugged-in devices and how it created device special files in the /dev directory.
We have also created custom udev rules of our own that are used to trigger events of various types. This capability enables us to exercise control over what happens when a device is plugged in to our Linux hosts to a degree that is impossible in most other PC operating systems.
This chapter once again merely provides a very brief experience with D-Bus and udev. There is much more that can be done using udev rules, but you should now at least be aware of some of the possibilities.

Exercises

Perform the following exercises to complete this chapter:
  1. 1.
    Describe the relationship between D-Bus and udev when a new hardware device is plugged into a Linux host.
     
  2. 2.
    List the steps taken by D-Bus and udev from when a USB thumb drive is plugged into a host until the device icon appears on the desktop.
     
  3. 3.
    Given that the udev action when a device like the USB drive used in this chapter is unplugged from the host is “removed”. Write a udev rule that adds a timestamp to the /tmp/udev.log file when the USB thumb drive is removed. Test to verify that this new rule works as expected.
     
Footnotes
3
Greg Kroah-Hartman, Kernel Korner – udev — Persistent Naming in User Space, Linux Journal, June, 2004, www.linuxjournal.com/article/7316
 
4
Kenlon, Seth, An introduction to Udev: The Linux subsystem for managing device events, https://opensource.com/article/18/11/udev
 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.193.129