In this article: Isn’t the whole system just a database? – libdrm, I explored a little bit of the database nature of Linux by using libudev to enumerate and open libdrm devices. After that, I spent some time bringing up a USB module: LJIT2libusb. libusb is a useful cross platform library that makes it relatively easy to gain access to the usb functions on multiple platforms. It can enumerate devices, deal with hot plug notifications, open up, read, write, etc.
At its core, on Linux at least, libusb tries to leverage the uvdev capabilities of the target system, if those capabilities are there. This means that device enumeration and hot plugging actually use the libuvdev stuff. In fact, the code for enumerating those usb devices in libusb looks like this:
udev_enumerate_add_match_subsystem(enumerator, "usb"); udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device"); udev_enumerate_scan_devices(enumerator); devices = udev_enumerate_get_list_entry(enumerator);
There’s more stuff of course, to turn that into data structures which are appropriate for use within the libusb view of the world. But, here’s the equivalent using LLUI and the previously developed UVDev stuff:
local function isUsbDevice(dev) if dev.IsInitialized and dev:getProperty("subsystem") == "usb" and dev:getProperty("devtype") == "usb_device" then return true; end return false; end each(print, filter(isUsbDevice, ctxt:devices()))
It’s just illustrative, but it’s fairly simple to understand I think. The ‘ctxt:devices()’ is an iterator over all the devices in the system. The ‘filter’ function is part of the luafun functional programming routines available to Lua. the ‘isUsbDevice’ is a predicate function, which returns ‘true’ when the device in question matches what it believes makes a device a ‘usb’ device. In this case, its the subsystem and dev_type properties which are used.
Being able to easily query devices like this makes life a heck of a lot easier. No funky code polluting my pure application. Just these simple query predicates written in Lua, and I’m all set. So, instead of relying on libusb to enumerate my usb devices, I can just enumerate them directly using uvdev, which is what the library does anyway. Enumeration and hotplug handing is part of the library. The other part is the actual send and receiving of data. For that, the libusb library is still primarily important, as replacing that code will take some time.
Where else can this great query capability be applied? Well, libudev is just a nice wrapper atop sysfs, which is that virtual file system built into Linux for gaining access to device information and control of the same. There’s all sorts of stuff in there. So, let’s say you want to list all the block devices?
local function isBlockDevice(dev) if dev.IsInitialized and dev:getProperty("subsystem") == "block" then return true; end return false; end
That will get all the devices which are in the subsystem “block”. That includes physical disks, virtual disks, partitions, and the like. If you’re after just the physical ones, then you might use something like this:
local function isPhysicalBlockDevice(dev) if dev.IsInitialized and dev:getProperty("subsystem") == "block" and dev:getProperty("devtype") == "disk" and dev:getProperty("ID_BUS") ~= nil then return true; end return false; end
Here, a physical device is indicated by subsystem == ‘block’ and devtype == ‘disk’ and the ‘ID_BUS’ property exists, assuming any physical disk would show up on one of the system’s buses. This won’t catch a SD card though. For that, you’d use the first one, and then look for a property related to being an SD card. Same goes for ‘cd’ vs ramdisk, or whatever. You can make these queries as complex or simple as you want.
Once you have a device, you can simply open it using the “SysName” parameter, handed to an fopen() call.
I find this to be a great way to program. It makes the creation of utilities such as ‘lsblk’ relatively easy. You would just look for all the block devices and their partitions, and put them into a table. Then separately, you would have a display routine, which would consume the table and generate whatever output you want. I find this much better than the typical Linux tools which try to do advanced display using the terminal window. That’s great as far as it goes, but not so great if what you really want is a nice html page generated for some remote viewing.
At any rate, this whole libudev exploration is a great thing. You can list all devices easily, getting every bit of information you care to examine. Since it’s all scriptable, it’s fairly easy to taylor your queries on the fly, looking at, discovering, and the like. I discovered that the thumb print reader in my old laptop was made by Broadcom, and my webcam by 3M? It’s just so much fun.
Well there you have it. The more you spelunk, the more you know, and the more you can fiddle about.