hotplugd(8) (opens new window) is a really small and useful utility looking for device events on OpenBSD. This tool is equivalent to devd(8) (opens new window) on FreeBSD and a bit similar to udev(7) (opens new window) on Linux.

hotplugd(8) (opens new window) will listen for the kernel events (based on the special file /dev/hotplug (opens new window) and execute a particular script with information about the device as arguments. When the device is attached, hotplugd will execute /etc/hotplug/attach and when the same device detach, it will execute /etc/hotplug/detach.

So, why this is really useful? Because you can create your own action based on the desired device! Furthermore, you can use any kind of language... If you want to use a shell, a perl or a python script, you can. Just be careful, hotplugd is running as root user by default, it means all your script can do bad things without protection.

Well, I will give you an example. I am using a 4G device (and sometime my smartphone) as router. When I plug the USB cable on my computer, and when I decide to share the connection, a urndis(4) (opens new window) interface is automatically created. This new interface name is a combination of the driver name and an index (as integer). In my case, it will create urndis0 interface.

hotplugd will offer you all required information about the device by sharing them directly as command arguments. The idea here is to use attach and detach scripts as command router. When the device is attached/detached, attach script will look linearly on different scripts and if one of them exists, it will execute it.

By priority order:

  • /etc/hotplug/device/${interface_name}
  • /etc/hotplug/device/${driver_name}
  • /etc/hotplug/class/${class_id}

Firstly, we will initialize the tree:

mkdir /etc/hotplug
mkdir /etc/hotplug/device
mkdir /etc/hotplug/class

# attach script
touch /etc/hotplug/attach
chmod 700 /etc/hotplug/attach

# detach script
touch /etc/hotplug/detach
chmod 700 /etc/hotplug/detach

# urndis0 script
touch /etc/hotplug/device/urndis0
chmod 700 /etc/hotplug/device/urndis0

We will create attach script located in /etc/hotplug/attach.

#!/bin/sh

DEVCLASS="${1}"
DEVNAME="${2}"
DEVDRIVER=$(echo ${DEVNAME} | sed -E 's/^([a-z]+)([0-9]+$)/\1/')
DEVINDEX=$(echo ${DEVNAME} | sed -E 's/^([a-z]+)([0-9]+$)/\2/')

if test -x /etc/hotplug/device/${DEVNAME}
then
    logger execute /etc/hotplug/device/${DEVNAME}
    /etc/hotplug/device/${DEVNAME} attach ${*}
elif test -x /etc/hotplug/device/${DEVDRIVE}
then
    logger execute /etc/hotplug/device/${DEVDRIVER}
    /etc/hotplug/device/${DEVDRIVER} attach ${*}
elif test -x /etc/hotplug/class/${DEVCLASS}
then
    logger execute /etc/hotplug/class/${DEVCLASS}
    /etc/hotplug/class/${DEVCLASS} attach ${*}
fi

Now the detach script located in /etc/hotplug/detach.

#!/bin/sh

DEVCLASS="${1}"
DEVNAME="${2}"
DEVDRIVER=$(echo ${DEVNAME} | sed -E 's/^([a-z]+)([0-9]+$)/\1/')
DEVINDEX=$(echo ${DEVNAME} | sed -E 's/^([a-z]+)([0-9]+$)/\2/')

if test -x /etc/hotplug/device/${DEVNAME}
then
    logger execute /etc/hotplug/device/${DEVNAME}
    /etc/hotplug/device/${DEVNAME} detach ${*}
elif test -x /etc/hotplug/device/${DEVDRIVE}
then
    logger execute /etc/hotplug/device/${DEVDRIVER}
    /etc/hotplug/device/${DEVDRIVER} detach ${*}
elif test -x /etc/hotplug/class/${DEVCLASS}
then
    logger execute /etc/hotplug/class/${DEVCLASS}
    /etc/hotplug/class/${DEVCLASS} detach ${*}
fi

We can now create ou script to manage urndis0 interface located in /etc/hotplug/device/urndis0. This script will assume we have configured urndis0 by creating /etc/hostname.urndis0 (opens new window) file.

#!/bin/sh

EVENT="${1}"
DEVCLASS="${2}"
DEVNAME="${3}"

_attach() {
    logger attach ${DEVNAME}
    sh /etc/netstart ${DEVNAME}
}
    
case "${EVENT}"
in
    attach) _attach;;
esac

Well, now it's the time to test it by enabling and starting hotplugd(8) (opens new window).

rcctl enable hotplugd
rcctl start hotplugd

Branch your device and follow /var/log/message log file.

tail -f /var/log/message

You should see something like that.

Jan 11 10:07:12 kin user: execute /etc/hotplug/device/urndis0
Jan 11 10:07:12 kin user: attach urndis0
Jan 11 10:07:13 kin dhclient[75457]: urndis0: 192.168.1.2 lease accepted from 192.168.1.1 (0a:6c:4b:1a:6d:35)

You can check directly with ifconfig(8) (opens new window) if it's right...

$ ifconfig urndis0
urndis0: flags=808843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,AUTOCONF4> mtu 1500
        lladdr 02:16:13:45:ac:de
        index 27 priority 0 llprio 3
        groups: egress
        inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.1

It works! But... What if I don't have the good device name? Okay, by default, I assume urndis devices are safe and plugged by me (damn, this is not the paranoid method). I will create another script called urndis located in /etc/hotplug/device/urndis. This one will execute dhclient(8) (opens new window) directly on the interface without using netstart(8) (opens new window).

#!/bin/sh

EVENT="${1}"
DEVCLASS="${2}"
DEVNAME="${3}"

_attach() {
    logger attach ${DEVNAME}
    dhclient ${DEVNAME}
}
    
case "${EVENT}"
in
    attach) _attach;;
esac
      

And when we disable temporarily /etc/hotplug/device/urndis0 by removing the execute mode...

chmod -x /etc/hotplug/device/urndis0

...we can now see that in /var/log/message.

Jan 11 10:54:11 kin user: urndis0 urndis 0
Jan 11 10:54:11 kin user: execute /etc/hotplug/device/urndis
Jan 11 10:54:11 kin /bsd: urndis0 at uhub0 port 1 configuration 1 interface 0 "Android" rev 2.00/ff.ff addr 3
Jan 11 10:54:11 kin /bsd: urndis0: using RNDIS, address 02:b6:13:63:74:6a
Jan 11 10:54:11 kin user: attach urndis0
Jan 11 10:54:11 kin dhclient[45455]: urndis0: 192.168.1.2 lease accepted from 192.168.11 (0a:12:ef:17:7d:23)

It seems to works! This is a simple way to connect device without any security and control.