This is an old revision of the document!


devd(8) is a tool that is include in the FreeBSD base system. It offer a way to the tools in userspace to run when a kernel event happen. For example, if we plug an smartphone and it is configured as a USB tethering, we could have a rule to devd(8) that runs dhclient(8) for that interface name.

The devd.conf configuration file is documented in devd.conf(5), where is the file that we need for defining rules to devd(8). In the base system, we alredy have one: /etc/devd.conf, that is not necessary to touch in most situations. Instead, we could use /usr/local/etc/devd directory and put our files with the .conf extension.

If we have a port that install a file there, probably, that directory was created, if not, we need to create it:

mkdir -p /usr/local/etc/devd

The configuration consists of two features: statements and comments. All statements end with a semicolon. Much statements have sub-statements, that likewise, end with a semicolon.

statement priority {
    substatement "value";
    ...
    substatement "value";
};

The statements can occur in any order in the configuration file and can be repeated so often as necessary.

Each statement has a priority, except the options (see below). The priority is an arbitrary number, where zero is lowest, and therefore, with a low priority. If two statements match, the statement where its priority is greatest, win.

The currently supported statements would be the following:

  • attach: When a device is plugged in and matches, an action is executed.
  • detach: Contrary to the above. When a device is unplugged and matches, an action is executed.
  • nomatch: This statements is triggered when a no device driver currently loaded in the kernel claims a new device.
  • notify: This statement can be the most useful in many circumstances, since it allows us to perform actions when the kernel sends an event to userspace. Normally, we can see this type of messages in /var/log/messages or by executing dmesg(8).
  • options: It specifies various options and parameters that we can use throughout the statements we create.

One way to understand the statements is to directly read /etc/devd.conf and review the above mentioned. It might also be useful to read what is in /etc/devd.conf and verify if a port we have installed, has created /usr/local/etc/devd, that is a good starting point.

options

  • directory "/some/path";: Specifies the directories to read and parse new statements with any file ending with the .conf extension. The default configuration file /etc/devd.conf already has two of these sub-statements in options to add directories /etc/devd and /usr/local/etc/devd.
  • pid-file "/var/run/devd.pid";: Specifies the file where the process ID is stored. Again, /etc/devd.conf has already one.
  • set regexp-name "(some|regexp)";: Create a regular expression that can be used in some sub-statements. If it begin with !, matches if a regular expression doesn't match. All regular expressions have an implicit $^ around it. In /etc/devd.conf we can found some pre-defined regular expressions, such as scsi-controller-regex and wifi-driver-regex. To use a regular expression in a sub-statement, the $ sign can be used (e.g.: device-name "$wifi-driver-regex";).

attach y detach

  • action "command";: Execute a command. For example: /etc/pccard_ether $device-name start.
  • class "string";: Shortcut to match "class" "string".
  • device-name "string";: Shortcut to match "device-name" "string". A variable called $device-name can be used if the sub-statement matches.
  • match "variable" "value";: Matches the content of value against variable. For example, match "device-name" "$wifi-driver-regex"; must match with the device name according to $wifi-driver-regex.
  • media-type "string";: For network devices, media-type matches devices with the following types:
    • Ethernet
    • Tokenring
    • FDDI
    • 802.11
    • ATM
  • subdevice "string";: Shortcut to match "subdevice" "string";.

nomatch

The following sub-statements have the same effect that attach and detach, so they will be mentioned without a description.

  • action "command";
  • match "variable" "value";

match

The following sub-statements have the same effect that the above sub-statement, so they will be mentioned without a description. The exception is action and match. The former is to offer an useful example and the latter is because match is something different.

  • action "command";: Execute a command. For example, action "service dhclient quietstart $subsystem". Other useful example is when we need to use the $notify variable that is relative to the system and subsystem that triggered the event.
  • match "system | subsystem | type | notify" "value";: An arbitrary number of match sub-statements can exists in a match statement and as other match sub-statements, value can be a fixed value or a regular expression.
  • media-type "string";

There is a significant number of variables, systems, subsystems, types and notifications that can be used in statements that use the match sub-statement, but this article cannot cover because they can be found in devd.conf(5). See sections Variables that can be used with the match statement and Notify matching in devd.conf(5).

The comments are trivial to C/C++ or Shell/Perl programmers.

C-style comments start with the two characters ‘/*’ (slash, star) and end with ‘*/’ (star, slash). This style can cover one line or multiple lines but cannot be nested.

/* This is the start of a comment.
    This is still part of the comment.
/* This is an incorrect attempt at nesting a comment. */
    This is no longer in any comment. */

C++-style comments start with the two characters ‘//’ (slash, slash) and continue to the end of the physical line. If we need to cover more than one line, is necessary to put again this type of comments in the next line.

// This is the start of a comment.  The next line
// is a new comment, even though it is logically
// part of the previous comment.

To avoid problems with special characters in the shell, here's what happens, say, $foo:

  1. The characters $' are inserted.
  2. The string $foo is removed.
  3. The value of the foo variable is inserted into the buffer with all single quote characters prefixed by a backslash.

In any context, these conditions are safe, but single quotes can cause unwanted effects. Suppose foo=meta and bar=var:

action "echo '$foo $bar'";

They will be present in the shell through the system(3) function:

echo '$'meta' $'var''

Which produces the following output:

echo $meta $var

Is this the result we want? Probably not. To produce a correct result, it is necessary to rewrite the rule as follows:

action "echo $foo' '$bar"

Be careful.

By default devd(8) runs as a service:

# service devd status
devd is running as pid 2310.

To understand what devd(8) does, we need to run it in the foreground instead of the background. Likewise, it is highly recommended to read /var/log/message and the output of dmesg(8) while writing and testing rules.

# service devd stop
Stopping devd.
Waiting for PIDS: 2310.
# devd -d
Parsing /etc/devd.conf
setting scsi-controller-regex=(aac|aacraid|ahc|ahd|amr|ciss|esp|ida|iir|ips|isp|mlx|mly|mpr|mps|mpt|sym|trm)[0-9]+
setting wifi-driver-regex=(ath|bwi|bwn|ipw|iwi|iwm|iwn|malo|mwl|otus|ral|rsu|rtwn|rum|run|uath|upgt|ural|urtw|wi|wpi|wtap|zyd)[0-9]+
Parsing files in /etc/devd
Parsing /etc/devd/uath.conf
Parsing /etc/devd/hyperv.conf
Parsing /etc/devd/zfs.conf
Parsing /etc/devd/iwmbtfw.conf
Parsing /etc/devd/asus.conf
Parsing /etc/devd/devmatch.conf
Parsing /etc/devd/ulpt.conf
Parsing files in /usr/local/etc/devd
Parsing /usr/local/etc/devd/cups.conf

The output is very descriptive. We see what devd(8) parses, which we can use for troubleshooting. It is highly recommended to run devd -d when we are creating a rule.

As mentioned, dmesg(8) and /var/log/messages can be our best friends. This is the output of dmesg(8) when a USB is connected:

umass0 on uhub0
umass0: <SanDisk Cruzer Blade, class 0/0, rev 2.00/1.27, addr 2> on usbus4
umass0:  SCSI over Bulk-Only; quirks = 0x8100
umass0:2:0: Attached to scbus2
da0 at umass-sim0 bus 0 scbus2 target 0 lun 0
da0: <SanDisk Cruzer Blade 1.27> Removable Direct Access SPC-4 SCSI device
da0: Serial Number 200515364102BCF2AC69
da0: 40.000MB/s transfers
da0: 7633MB (15633408 512 byte sectors)
da0: quirks=0x2<NO_6_BYTE>

We have enough information to create statements. We will create usb_attach.conf in /usr/local/etc/devd as an example.

/usr/local/etc/devd/usb_attach.conf:

attach 100 {
        device-name "umass0";
        action "logger $device-name is plugged in";
};

detach 100 {
        device-name "umass0";
        action "logger $device-name is unplugged";
};

When a devices matches umass0, its name is printed using logger(1). We can see the message in /var/log/message:

Jul 11 15:57:01 dtxdf-laptop dtxdf-fbsd[4832]: umass0 is unplugged
Jul 11 15:57:01 dtxdf-laptop kernel: ugen4.2: <SanDisk Cruzer Blade> at usbus4 (disconnected)
Jul 11 15:57:01 dtxdf-laptop kernel: umass0: at uhub0, port 2, addr 2 (disconnected)
Jul 11 15:57:01 dtxdf-laptop kernel: da0 at umass-sim0 bus 0 scbus2 target 0 lun 0
Jul 11 15:57:01 dtxdf-laptop kernel: da0: <SanDisk Cruzer Blade 1.27>  s/n 200515364102BCF2AC69 detached
Jul 11 15:57:01 dtxdf-laptop kernel: (da0:umass-sim0:0:0:0): Periph destroyed
Jul 11 15:57:01 dtxdf-laptop kernel: umass0: detached
Jul 11 15:57:07 dtxdf-laptop dtxdf-fbsd[4836]: umass0 is plugged in
Jul 11 15:57:07 dtxdf-laptop kernel: ugen4.2: <SanDisk Cruzer Blade> at usbus4
Jul 11 15:57:07 dtxdf-laptop kernel: umass0 on uhub0
Jul 11 15:57:07 dtxdf-laptop kernel: umass0: <SanDisk Cruzer Blade, class 0/0, rev 2.00/1.27, addr 2> on usbus4
Jul 11 15:57:07 dtxdf-laptop kernel: umass0:  SCSI over Bulk-Only; quirks = 0x8100
Jul 11 15:57:07 dtxdf-laptop kernel: umass0:2:0: Attached to scbus2
Jul 11 15:57:08 dtxdf-laptop kernel: da0 at umass-sim0 bus 0 scbus2 target 0 lun 0
Jul 11 15:57:08 dtxdf-laptop kernel: da0: <SanDisk Cruzer Blade 1.27> Removable Direct Access SPC-4 SCSI device
Jul 11 15:57:08 dtxdf-laptop kernel: da0: Serial Number 200515364102BCF2AC69
Jul 11 15:57:08 dtxdf-laptop kernel: da0: 40.000MB/s transfers
Jul 11 15:57:08 dtxdf-laptop kernel: da0: 7633MB (15633408 512 byte sectors)
Jul 11 15:57:08 dtxdf-laptop kernel: da0: quirks=0x2<NO_6_BYTE>

This example can be extended, for example, to mount a partition when a device is plugged in. To avoid problems with device names /dev/da[0-9], it is strongly recommended to partition and label them. To simplify this task, we can use diskinfo(8) to get the serial number of our device to use as a unique label in our inventory.

$ diksinfo -s da0
200515364102BCF2AC69

The serial number is very large, but the final characters can be used. To finish the label, we could concatenate a logical abbreviation of the purpose of the partition and the final five characters. The end result is:

rt2AC69

We are already to partition:

# gpart create -s gpt da0
da0 created
# gpart add -t freebsd-ufs -l rt2AC69 da0
da0p1 added
# newfs -j /dev/gpt/rt2AC69
/dev/gpt/rt2AC69: 7633.5MB (15633328 sectors) block size 32768, fragment size 4096
        using 13 cylinder groups of 625.22MB, 20007 blks, 80128 inodes.
        with soft updates
super-block backups (for fsck_ffs -b #) at:
 192, 1280640, 2561088, 3841536, 5121984, 6402432, 7682880, 8963328, 10243776, 11524224, 12804672, 14085120, 15365568
Using inode 4 in cg 0 for 33554432 byte journal
newfs: soft updates journaling set
# mount /dev/gpt/rt2AC69 /mnt
# echo "Hello!" > /mnt/hello.txt
# cat /mnt/hello.txt
Hello!
# umount /mnt

Here the da0 device with GPT is used and unique partition is added. That partition was formatted and mounted on /mnt to write a file called hello.txt. Last, the partition is unmounted.

Unplug the device and plug in it again. The label is also useful for debugging, as it will print to the devd(8) output.

Processing event '!system=GEOM subsystem=DEV type=CREATE cdev=gpt/rt2AC69'
Pushing table
setting *=!system=GEOM subsystem=DEV type=CREATE cdev=gpt/rt2AC69
setting _=system=GEOM subsystem=DEV type=CREATE cdev=gpt/rt2AC69
setting timestamp=1657571585.404812
setting system=GEOM
setting subsystem=DEV
setting type=CREATE
setting cdev=gpt/rt2AC69
Processing notify event
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^ETHERNET$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^HYPERV_NIC_VF$, invert=0
Testing system=GEOM against ^ETHERNET$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^ZFS$, invert=0
Testing system=GEOM against ^IFNET$, invert=0
Testing system=GEOM against ^IFNET$, invert=0
Testing system=GEOM against ^IFNET$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Testing system=GEOM against ^ACPI$, invert=0
Popping table

When we plug in our USB, the output of devd(8) is overwhelmingly large, so using a tool like screen(1) or tmux(1) is highly recommended, as either of them have a feature called Copy Mode that is useful for searching the output buffer. However, traditional tools like script(1) with a pager like less(1) or more(1) can be used for this purpose.

The idea is to search in the output of devd(8) to get the exact point of the event we need. When we've done that, we need to know if the parameters that devd(8) is using are the required ones. In this example, the following parameters are relevant:

setting system=GEOM
setting subsystem=DEV
setting type=CREATE
setting cdev=gpt/rt2AC69

See the “Variables and the notify event” for more details on those parameters.

We need to modify usb_attach.conf with the parameters described above:

notify 100 {
        match "system" "GEOM";
        match "subsystem" "DEV";
        match "type" "CREATE";
        match "cdev" "gpt/rt2AC69";
        action "sleep 2 && /usr/local/bin/scripts/automount.sh $cdev";
};

notify 100 {
        match "system" "GEOM";
        match "subsystem" "DEV";
        match "type" "DESTROY";
        match "cdev" "gpt/rt2AC69";
        action "sleep 2 && /usr/local/bin/scripts/autoumount.sh $cdev";
};

In many cases we need to do something and, in turn, its counterpart. In this case, we need two scripts: the first is to create a directory and mount its partition, and the second is to delete the directory and forcibly unmount it. Forcibly unmounting the file system seems a bad idea, but since the device was unplugged without unmounting its partition, it's necessary.

automount.sh:

#!/bin/sh

set -e

: ${RTDIR:=/mnt}

dev=$1
if [ -z "${dev}" ]; then
    echo "usage: automount.sh device" >&2
    exit 1
fi

_dev=`basename "${dev}"`
mntdir="${RTDIR}/${_dev}"

fsck -p "/dev/${dev}"
mkdir -p "${mntdir}"
mount "/dev/${dev}" "${mntdir}"

autoumount.sh:

#!/bin/sh

: ${RTDIR:=/mnt}

dev=$1
if [ -z "${dev}" ]; then
    echo "usage: autoumount.sh device" >&2
    exit 1
fi

_dev=`basename "${dev}"`
mntdir="${RTDIR}/${_dev}"

umount -f "${mntdir}" 2> /dev/null
rmdir "${mntdir}"

When the device is plugged in, devd(8) automatically mounts its partition.

Processing event '!system=GEOM subsystem=DEV type=CREATE cdev=gpt/rt2AC69'
Pushing table
setting *=!system=GEOM subsystem=DEV type=CREATE cdev=gpt/rt2AC69
setting _=system=GEOM subsystem=DEV type=CREATE cdev=gpt/rt2AC69
setting timestamp=1657575727.806329
setting system=GEOM
setting subsystem=DEV
setting type=CREATE
setting cdev=gpt/rt2AC69
Processing notify event
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^DEVFS$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^USB$, invert=0
Testing system=GEOM against ^GEOM$, invert=0
Testing subsystem=DEV against ^DEV$, invert=0
Testing type=CREATE against ^CREATE$, invert=0
Testing cdev=gpt/rt2AC69 against ^gpt/rt2AC69$, invert=0
Executing 'sleep 2 && /usr/local/bin/scripts/automount.sh $'gpt/rt2AC69''
/dev/gpt/rt2AC69: FILE SYSTEM CLEAN; SKIPPING CHECKS
/dev/gpt/rt2AC69: clean, 1880626 free (26 frags, 235075 blocks, 0.0% fragmentation)
Popping table
cat /mnt/rt2AC69/hello.txt
Hello!

There is an implementation in the FreeBSD base system that is better than our automounter, called automountd(8), but for teaching purposes it was a perfect way to understand devd(8).

However, No need to reinvent the wheel can be applied to the new rules for devd(8). Some rules that we can find in /etc/devd can be changed a bit to suit our needs.

An example is when using USB tethering from an Android device which creates an urndis(8) interface, which in turn can be configured using dhclient(8).

If we connect our Android device to our machine, we will see useful information:

Pushing table
setting *=!system=IFNET subsystem=ue0 type=ATTACH
setting _=system=IFNET subsystem=ue0 type=ATTACH
setting timestamp=1657576470.672852
setting system=IFNET
setting subsystem=ue0
setting type=ATTACH
Processing notify event
Testing system=IFNET against ^DEVFS$, invert=0
Testing system=IFNET against ^DEVFS$, invert=0
Testing system=IFNET against ^DEVFS$, invert=0
Testing system=IFNET against ^DEVFS$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^USB$, invert=0
Testing system=IFNET against ^GEOM$, invert=0
Testing system=IFNET against ^GEOM$, invert=0
Testing system=IFNET against ^DEVFS$, invert=0
Testing system=IFNET against ^DEVFS$, invert=0
Testing system=IFNET against ^ACPI$, invert=0
Testing system=IFNET against ^ACPI$, invert=0
Testing system=IFNET against ^ACPI$, invert=0
Testing system=IFNET against ^ACPI$, invert=0
Testing system=IFNET against ^DEVFS$, invert=0
Testing system=IFNET against ^DEVFS$, invert=0
Testing system=IFNET against ^HYPERV_NIC_VF$, invert=0
Testing system=IFNET against ^ETHERNET$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^ZFS$, invert=0
Testing system=IFNET against ^IFNET$, invert=0
Testing subsystem=ue0 against ^(usbus|wlan)[0-9]+$, invert=1
Testing type=ATTACH against ^ATTACH$, invert=0
Executing '/etc/pccard_ether $'ue0' start'
Starting Network: ue0.
ue0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
        ether 00:00:00:00:00:00
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
Popping table
Processing event '!system=ETHERNET subsystem=ue0 type=IFATTACH'
Pushing table
setting *=!system=ETHERNET subsystem=ue0 type=IFATTACH
setting _=system=ETHERNET subsystem=ue0 type=IFATTACH
setting timestamp=1657576471.170142
setting system=ETHERNET
setting subsystem=ue0
setting type=IFATTACH
Processing notify event
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^GEOM$, invert=0
Testing system=ETHERNET against ^GEOM$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^ACPI$, invert=0
Testing system=ETHERNET against ^ACPI$, invert=0
Testing system=ETHERNET against ^ACPI$, invert=0
Testing system=ETHERNET against ^ACPI$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^HYPERV_NIC_VF$, invert=0
Testing system=ETHERNET against ^ETHERNET$, invert=0
Testing type=IFATTACH against ^IFATTACH$, invert=0
Executing '/usr/libexec/hyperv/hyperv_vfattach $'ue0' 0'
Popping table

There is a rule that matches and runs the /usr/libexec/hyperv/hyper_vfattach tool. If we want to know what file is using that tool, we can use grep(1):

$ egrep -r '/usr/libexec/hyperv/hyperv_vfattach' /etc/devd
/etc/devd/hyperv.conf:  action "/usr/libexec/hyperv/hyperv_vfattach $subsystem 0";

We must remember that /etc/devd is not the only directory used for devd(8), so it is important to search all directories used by the options statement.

/etc/devd/hyperv.conf:

notify 10 {
        match "system"          "ETHERNET";
        match "type"            "IFATTACH";
        action "/usr/libexec/hyperv/hyperv_vfattach $subsystem 0";
};

Now it's trivial to add a new rule:

/usr/local/etc/devd/usb_tethering.conf:

notify 20 {
        match "system"          "ETHERNET";
        match "type"            "IFATTACH";
        match "subsystem"       "ue[0-9]+";
        action "/sbin/dhclient $subsystem";
};

We must increase the priority and add the subsystem for urndis(4). We also set dhclient(8) to action and when our device is plugged in, we should have Internet connection automatically.

Processing event '!system=ETHERNET subsystem=ue0 type=IFATTACH'
Pushing table
setting *=!system=ETHERNET subsystem=ue0 type=IFATTACH
setting _=system=ETHERNET subsystem=ue0 type=IFATTACH
setting timestamp=1657577216.269419
setting system=ETHERNET
setting subsystem=ue0
setting type=IFATTACH
Processing notify event
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^DEVFS$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^USB$, invert=0
Testing system=ETHERNET against ^GEOM$, invert=0
Testing system=ETHERNET against ^GEOM$, invert=0
Testing system=ETHERNET against ^ETHERNET$, invert=0
Testing type=IFATTACH against ^IFATTACH$, invert=0
Testing subsystem=ue0 against ^ue[0-9]+$, invert=0
Executing '/sbin/dhclient $'ue0''
DHCPDISCOVER on ue0 to 255.255.255.255 port 67 interval 8
DHCPOFFER from 192.168.42.129
DHCPREQUEST on ue0 to 255.255.255.255 port 67
DHCPACK from 192.168.42.129
bound to 192.168.42.2 -- renewal in 1800 seconds.
Popping table

Now we can use devd(8) to exploit our dynamic hardware control, but remember that it is not a substitute for man pages, so please read also devd(8), devd.conf(5) and devctl(8).


  • devd.1663546760.txt.gz
  • Last modified: 2022/09/19 00:19
  • by KubilayKocak