Simple udev implementation in Golang developed from scratch.
This library allow to listen and manage Linux-kernel (since version 2.6.10) Netlink messages to user space (ie: NETLINK_KOBJECT_UEVENT).
Like udev you will be able to monitor, display and manage devices plug to the system.
Monitoring requires a Linux kernel: on other platforms the module still builds and the uevent parsing/matching API stays
available, but UEventConn methods return netlink.ErrNotSupported.
Use the library:
go get github.com/pilebones/go-udev
Or install the command line tool:
go install github.com/pilebones/go-udev/cmd/go-udev@latest
The library itself has no external dependency.
go test ./...
go build ./cmd/go-udev
./go-udev -<mode> [-file=<absolute_path>]
Allowed mode: info or monitor
File should contains matcher rules (see: "Advanced usage" section)
Crawl /sys/devices uevent struct to detect plugged devices:
./go-udev -info
Handle all kernel message to detect change about plugged or unplugged devices:
./go-udev -monitor
Only the events sent by the kernel or by udevd are delivered, the others are dropped and reported as
netlink.ErrUntrustedSender: an uevent is trivial to forge for a process able to write on the socket, and
consumers usually act on the devices it announces.
Example of output when a USB storage is plugged:
2017/10/20 23:47:23 Handle UEvent{Action: add, KObj: /devices/pci0000:00/0000:00:14.0/usb1/1-1, Env: {ACTION="add", BUSNUM="001", DEVNAME="bus/usb/001/005", DEVNUM="005", DEVPATH="/devices/pci0000:00/0000:00:14.0/usb1/1-1", DEVTYPE="usb_device", MAJOR="189", MINOR="4", PRODUCT="58f/6387/10b", SEQNUM="2511", SUBSYSTEM="usb", TYPE="0/0/0"}}
2017/10/20 23:47:23 Handle UEvent{Action: add, KObj: /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0, Env: {ACTION="add", DEVPATH="/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0", DEVTYPE="usb_interface", INTERFACE="8/6/80", MODALIAS="usb:v058Fp6387d010Bdc00dsc00dp00ic08isc06ip50in00", PRODUCT="58f/6387/10b", SEQNUM="2512", SUBSYSTEM="usb", TYPE="0/0/0"}}
2017/10/20 23:47:23 Handle UEvent{Action: add, KObj: /module/usb_storage, Env: {ACTION="add", DEVPATH="/module/usb_storage", SEQNUM="2513", SUBSYSTEM="module"}}
[...]
Example of output when a USB storage is unplugged:
[...]
2017/10/20 23:47:29 Handle UEvent{Action: remove, KObj: /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/host4/target4:0:0/4:0:0:0/block/sdb, Env: {ACTION="remove", DEVNAME="sdb", DEVPATH="/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/host4/target4:0:0/4:0:0:0/block/sdb", DEVTYPE="disk", MAJOR="8", MINOR="16", SEQNUM="2543", SUBSYSTEM="block"}}
[...]
2017/10/20 23:47:29 Handle UEvent{Action: remove, KObj: /devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0, Env: {ACTION="remove", DEVPATH="/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0", DEVTYPE="usb_interface", INTERFACE="8/6/80", MODALIAS="usb:v058Fp6387d010Bdc00dsc00dp00ic08isc06ip50in00", PRODUCT="58f/6387/10b", SEQNUM="2548", SUBSYSTEM="usb", TYPE="0/0/0"}}
2017/10/20 23:47:29 Handle UEvent{Action: remove, KObj: /devices/pci0000:00/0000:00:14.0/usb1/1-1, Env: {ACTION="remove", BUSNUM="001", DEVNAME="bus/usb/001/005", DEVNUM="005", DEVPATH="/devices/pci0000:00/0000:00:14.0/usb1/1-1", DEVTYPE="usb_device", MAJOR="189", MINOR="4", PRODUCT="58f/6387/10b", SEQNUM="2549", SUBSYSTEM="usb", TYPE="0/0/0"}}
Note: To implement your own monitoring system, please see cmd/go-udev/main.go as a simple example.
Is it possible to filter uevents/devices with a Matcher.
A Matcher is a list of your own rules to match only relevant uevent kernel message (see: matcher.sample).
You could pass this file using for both mode:
./go-udev -file matcher.sample [...]
Monitoring stops as soon as the context is cancelled, and the queue is closed when the worker returns:
conn := new(netlink.UEventConn)
if err := conn.Connect(netlink.UdevEvent); err != nil {
log.Fatalln("unable to subscribe to netlink uevent, err:", err)
}
defer conn.Close()
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
queue, errs := make(chan netlink.UEvent), make(chan error)
go conn.MonitorWithContext(ctx, queue, errs, nil) // pass a netlink.Matcher to filter the events
for {
select {
case uevent, more := <-queue:
if !more {
return
}
log.Println("handle", uevent.Action, uevent.KObj)
case err := <-errs:
// Reading errors are not fatal, a dropped message does not stop the monitoring
log.Println("ERROR:", err)
}
}docker build . -t go-udev
docker run --rm -t go-udev -info
docker run --rm -v=/dev:/dev -v /run/udev:/run/udev:ro --net=host -t go-udev -monitor
Warning: unsafe method because we share host /dev directory to the container.
TODO: unsafe and -monitor is not fully functional in docker, fix working in progress...
Don't hesitate to notice if you detect a problem with this tool or library.
- Netlink Manual: http://man7.org/linux/man-pages/man7/netlink.7.html
- Linux source code about:
- Struct sockaddr_netlink: http://elixir.free-electrons.com/linux/v3.12/source/lib/kobject_uevent.c#L45
- KObject action: http://elixir.free-electrons.com/linux/v3.12/source/lib/kobject_uevent.c#L45
go-udev is available under the GNU GPL v3 - Clause License.