===== devd: device state change daemon =====
===== Introduction =====
''%%devd(8)%%'' is a tool included in the %%FreeBSD%% base system. It provides a way of running userland tools when a specific kernel event happens. For example, if plugging a smartphone configured for USB tethering, we could have a ''%%devd(8)%%'' rule that runs ''%%dhclient(8)%%'' for that interface name.
===== devd.conf configuration =====
''%%devd.conf(5)%%'' is the configuration file for defining ''%%devd(8)%%'' rules. There's already one in the base system: ''%%/etc/devd.conf%%'', which can be left unmodified in most situations. Instead, let's use the ''%%/usr/local/etc/devd%%'' directory for files with the .conf extension.
If a port or package installed a file there, that directory should already exist; otherwise, create it:
# mkdir -p /usr/local/etc/devd
The configuration consists of two features: statements and comments. All statements end with a semicolon. Many statements have substatements, that also end with a semicolon.
statement priority {
substatement "value";
...
substatement "value";
};
Statements can occur in any order in the configuration file and can be repeated as often as necessary.
Each statement has a priority, except the options (see below). The priority is an arbitrary number, where zero is the lowest priority. If two or more statements match, only the statement with the greatest priority is processed.
The currently supported statements are as follows:
* ''%%attach%%'': When a device is plugged in and matches, perform an action.
* ''%%detach%%'': Reverse of the above. When a device is unplugged and matches, perform an action.
* ''%%nomatch%%'': This statement is triggered when no device driver currently loaded in the kernel claims a new device.
* ''%%notify%%'': This statement is very useful in many cases, since it allows us to perform actions when the kernel sends an event to userland. Normally, we can see this type of messages in ''%%/var/log/messages%%'' or by executing ''%%dmesg(8)%%''.
* ''%%options%%'': This sets 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 ''%%/etc/devd.conf%%'' and verify if an installed port or package we have installed created ''%%/usr/local/etc/devd%%''; that is a good starting point.
==== Sub-Statements ====
=== options ===
* ''%%directory "/some/path";%%'': Adds ''%%/some/path%%'' to the directories to read ''%%*.conf%%'' files from. 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%%'' already has one.
* ''%%set regexp-name "(some|regexp)";%%'': Create a regular expression that can be used in sub-statements later in the file. If it begins with ''%%!%%'', it matches if "(some|regexp)" doesn't match. All regular expressions have an implicit ''%%$^%%'' around them. In ''%%/etc/devd.conf%%'' we can find 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 and detach ===
* ''%%action "command";%%'': Execute a command. For example: ''%%/etc/pccard_ether $device-name start%%''.
* ''%%class "string";%%'': Shortcut for ''%%match "class" "string"%%''.
* ''%%device-name "string";%%'': Shortcut for ''%%match "device-name" "string";%%''. A variable called `$device-name is set 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 (''%%devd.conf(5)%%'' in 12.3 and 13.1 still lists Tokenring and FDDI, which are no longer allowed):
* ''%%Ethernet%%''
* ''%%802.11%%''
* ''%%ATM%%''
* ''%%unknown%%''
* ''%%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 first is to offer an useful example and the second 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";%%''
==== Variables and the notify event ====
There are a lot of variables, systems, subsystems, types and notifications that can be used in statements that include a ''%%match%%'' sub-statement, but this article need not cover them 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)%%''.
==== Comments ====
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.
The above may be written using perl-style comments:
# This is the start of a comment. The next line
# is a new comment, even though it is logically
# part of the previous comment.
==== Notes on Variable Expansion ====
To avoid problems with shell special characters in action sub-statements, here's what happens to, say, ''%%$foo%%'':
- The characters ''%%$'%%'' are inserted.
- The string ''%%$foo%%'' is removed.
- The value of the foo variable is inserted into the buffer with all single quote characters prefixed by a backslash.
See sh(1) for what this construct means. 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.
===== devd(8) in debug mode =====
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/messages%%'' 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.
As mentioned, ''%%dmesg(8)%%'' and ''%%/var/log/messages%%'' can be our best friends. This is the output of ''%%dmesg(8)%%'' when a USB key is connected:
umass0 on uhub0
umass0: 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: 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
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 device matches ''%%umass0%%'', its name is printed using ''%%logger(1)%%''. We can see the message in ''%%/var/log/messages%%'':
Jul 11 15:57:01 dtxdf-laptop dtxdf-fbsd[4832]: umass0 is unplugged
Jul 11 15:57:01 dtxdf-laptop kernel: ugen4.2: 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: 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: at usbus4
Jul 11 15:57:07 dtxdf-laptop kernel: umass0 on uhub0
Jul 11 15:57:07 dtxdf-laptop kernel: umass0: 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: 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
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.
$ diskinfo -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 ready 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 a single partition is added. That partition is formatted and mounted on ''%%/mnt%%'' to write a file called ''%%hello.txt%%''. Last, the partition is unmounted.
Unplug the device and plug it back in. The label is also useful for debugging, as it will be printed in 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 key, ''%%devd(8)%%'' output is very large, so using a tool like ''%%screen(1)%%'' or ''%%tmux(1)%%'' is highly recommended, as both 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 ''%%devd(8)%%'' output to get the exact point of the event we need. When we've done that, we need to check whether 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 "[[#Variables_and_the_notify_event|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 no need to reinvent the wheel =====
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 metric 0 mtu 1500
ether 00:00:00:00:00:00
nd6 options=29
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 an 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
===== Afterword =====
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)%%''.
----
{{tag>HowTo}}