This is an old revision of the document!
Hotplug
Procd (the init system and process management daemon) executes scripts located in /etc/hotplug.d/ when certain events happen, like for example when an interface goes up or down, when a new storage drive is detected, or when a button is pressed.
It can be very useful with PPPoE connection or in an unstable network, or to use hardware buttons.
This functionality emulates/extends what was done by the long retired Hotplug2 package.
How it works
In the /etc/hotplug.d folder you will find some subfolders block iface, net and ntp.
When the trigger event fires, Procd will execute all scripts in that trigger's subfolder, in alphabetical order. Which is why most scripts in there use a numeric prefix.
- block folder is for block device events (block device connected/disconnected).
- iface folder is for interface events (when a interface like LAN or WAN is connected/disconnected)
- net folder is for
(probably network-related)
- ntp folder is for time sync events (time step, time server stratum change)
- button folder is for buttons (not created by default, see /etc/rc.button instead)
- usb folder is for usb devices like 3g-modem and tty*
There may (should) be others, for other types of triggers. By looking at the source in git there should be some also for buttons, sound devices, serial and usb serial dongles.
Usage
Simply place your script(s) into the right hotplug.d subdirectory, if there is none just create the right one.
Information provided to your scripts / Troubleshooting
Procd exposes a wealth of info when executes your scripts in hotplug.d, usually as environmental variables.
If you want to see what environmental variables it is providing, make a script that contains this line:
env > /tmp/envs_log.log
and place it in the folder you want to use, then trigger the event connected to that folder, and then you can see what envs were passed by reading the /tmp/envs_log.log text file
The block folder
For scripts in block folder, these are the (relevant) environmental variables
| Variable name | Description |
|---|---|
| ACTION | Either “add” or “remove” |
| DEVICENAME | seems same as DEVNAME below |
| DEVNAME | Device or partition name (if I connect a drive I get a hotplug call with “sda” and another hotplug call with “sda1”) |
| DEVPATH | full device path (for example “/devices/pci0000:00/0000:00:0b.0/usb1/1-1/1-1:1.0/host7/target7:0:0/7:0:0:0/block/sdc/sdc1 ” |
| DEVTYPE | what the DEVNAME e DEVICENAME are names of, I've seen “partition” here when a device with a readable partition is inserted and a “disk” when that device is removed. |
| MAJOR | major device number |
| MINOR | minor device number |
| SEQNUM | seqnum (a number) |
| SUBSYSTEM | seems this is only “block” |
The iface folder
There are three main environment variables that are passed to each iface hotplug script:
| Variable name | Description |
|---|---|
| ACTION | Either “ifup” or “ifdown” |
| INTERFACE | Name of the interface which went up or down (e.g. “wan” or “ppp0”) |
| DEVICE | Physical device name which interface went up or down (e.g. “eth0.1” or “br-lan”) |
The ntp folder
| Variable name | description |
|---|---|
| ACTION | step, stratum, unsync or periodic |
| freq_drift_ppm | ntp variables |
| offset | ntp variables |
| stratum | ntp variables |
| poll_interval | ntp variables |
Even without NTP sync, you will receive a periodic hotplug event, with stratum=16, about every 11 minutes out of the box.
The usb folder
| Variable name | description |
|---|---|
| ACTION | add, remove as above |
| DEVNAME | eg, “bus/usb/001/002” |
| DEVPATH | eg, “/devices/platform/ehci-platform/usb1/1-1” |
| DEVICENAME | eg “1-1” |
| DEVNUM | eg 002 |
| DRIVER | “usb” |
| TYPE | eg 9/0/1 |
| PRODUCT | the vendor/productcode/version, eg “424/2640/0” see lsusb |
| SEQNUM | ? eg 335 |
| BUSNUM | eg 001 |
| MAJOR | eg 189 |
| MINOR | eg 1 |
Examples
cat << "EOF" > /etc/hotplug.d/iface/99-my-action [ "${ACTION}" = "ifup" ] && { logger -t button-hotplug "Device: ${DEVICE} / Action: ${ACTION}" } EOF
Every time an interface goes up then the if/fi statement will be executed.
Restart wlan on USB WiFi hotplug
Niii has posted this quick example for a USB WiFi device hotplug event to trigger an init.d network restart wlan0 script.
To determine RTL8188SU_PRODID variable:
# lsusb -v idVendor 0x0bda Realtek Semiconductor Corp. idProduct 0x8171 RTL8188SU 802.11n WLAN Adapter bcdDevice 2.00
cat << "EOF" > /etc/hotplug.d/usb/20-rtl8188su BINARY="/sbin/wifi up" RTL8188SU_PRODID="bda/8171/200" if [ "${PRODUCT}" = "${RTL8188SU_PRODID}" ]; then if [ "${ACTION}" = "add" ]; then ${BINARY} fi fi EOF
Symlink instead of rename a device
An other script to create a symlink instead of renaming the device.
I test if DEVICE_NAME is empty because when I plug usb device I retrieve two add event, and the first come before created device, so symlink fails.
cat << "EOF" > /etc/hotplug.d/usb/20-cp210x CP210_PRODID="10c4/ea60/100" SYMLINK="my_link" if [ "${PRODUCT}" = "${CP210_PRODID}" ]; then if [ "${ACTION}" = "add" ]; then DEVICE_NAME=$(ls /sys/${DEVPATH} | grep tty) if [ -z ${DEVICE_NAME} ]; then logger -t Hotplug Warning DEVICE_NAME is empty exit fi logger -t Hotplug Device name of cp210 is ${DEVICE_NAME} ln -s /dev/${DEVICE_NAME} /dev/${SYMLINK} logger -t Hotplug Symlink from /dev/${DEVICE_NAME} to /dev/${SYMLINK} created fi fi if [ "${PRODUCT}" = "${CP210_PRODID}" ]; then if [ "${ACTION}" = "remove" ]; then rm /dev/${SYMLINK} logger -t Hotplug Symlink /dev/${SYMLINK} removed fi fi EOF
Script that detects if plugged usb device is bluetooth or not.
cat << "EOF" > /etc/hotplug.d/usb/20-bt_test BT_PRODID="a12/1/" BT_PRODID_HOT=`echo ${PRODUCT} | cut -c 1-6` #logger -t HOTPLUG "PRODUCT ID is" ${BT_PRODID_HOT} if [ "${BT_PRODID_HOT}" = "${BT_PRODID}" ]; then if [ "${ACTION}" = "add" ]; then logger -t HOTPLUG "bluetooth device has been plugged in!" if [ "${BSBTID_NEW}" = "${BSBTID_OLD}" ]; then logger -t HOTPLUG "bluetooth device hasn't changed" else logger -t HOTPLUG "bluetooth device has changed" fi fi if [ "${ACTION}" = "remove" ]; then logger -t HOTPLUG "bluetooth device has been removed!" fi else logger -t HOTPLUG "USB device is not bluetooth" fi EOF
Auto start mjpg-streamer when an usb camera is plugged in.
cat << "EOF" > /etc/hotplug.d/usb/20-mjpg_start case "${ACTION}" in add) # start process /etc/init.d/mjpg-streamer start ;; remove) # stop process /etc/init.d/mjpg-streamer stop ;; esac EOF
Custom automount script for xfs
cat << "EOF" > /etc/hotplug.d/block/xfs_automount # if a new block device is connected if [ "${ACTION}" = "add" ] ; then # getting device UUID detected_uuid="$(xfs_admin -u /dev/${DEVICENAME} | awk '{print $3}')" # deciding mountpoint for known UUID mountpoint="" case "${detected_uuid}" in 6a5d7c5c-c9d0-41cc-8f19-78d97f839c05) mountpoint="/path/to/first/mountpoint" ;; 02880b1f-0c67-46b6-9b05-5535680ccc89) mountpoint="/path/to/second/mountpoint" ;; esac # if we have a known UUID we have a mountpoint so we can mount it if [ "${mountpoint}" != "" ] ; then mount /dev/${DEVICENAME} ${mountpoint} fi fi # unmounting happens automatically at device disconnection anyway so no logic for that EOF
Coldplug
If you had notice the udev and eudev were removed in the openwrt 18.0.* release, don't be afraid because you still can make the things works.
Using hotplug scripts as coldplug
You just need to pay atention at the ACTION env var, at the boot are executed 'bind' actions.
So, just add this option to hotplug run accordinly. In my case I used this:
cat << "EOF" > /etc/hotplug.d/usb/22-symlinks # Description: Action executed on boot (bind) and with the system on the fly if [ "${ACTION}" = "bind" ] ; then case "${PRODUCT}" in 1bc7*) # Telit HE910 3g modules product id prefix DEVICE_NAME="$(ls /sys/$DEVPATH | grep tty)" DEVICE_TTY="$(ls /sys/$DEVPATH/tty/)" # Module Telit HE910-* connected to minipciexpress slot MAIN if [ "${DEVICENAME}" = "1-1.3:1.0" ] ; then ln -s /dev/${DEVICE_TTY} /dev/ttyMODULO1_DIAL logger -t Hotplug "Symlink from /dev/${DEVICE_TTY} to /dev/ttyMODULO1_DIAL created" elif [ "${DEVICENAME}" = "1-1.3:1.6" ] ; then ln -s /dev/${DEVICE_TTY} /dev/ttyMODULO1_DATA logger -t Hotplug "Symlink from /dev/${DEVICE_TTY} to /dev/ttyMODULO1_DATA created" # Module Telit HE910-* connected to minipciexpress slot SECONDARY elif [ "${DEVICENAME}" = "1-1.2:1.0" ] ; then ln -s /dev/${DEVICE_TTY} /dev/ttyMODULO2_DIAL logger -t Hotplug "Symlink from /dev/${DEVICE_TTY} to /dev/ttyMODULO2_DIAL created" elif [ "${DEVICENAME}" = "1-1.2:1.6" ] ; then ln -s /dev/${DEVICE_TTY} /dev/ttyMODULO2_DATA logger -t Hotplug "Symlink from /dev/${DEVICE_TTY} to /dev/ttyMODULO2_DATA created" fi ;; esac fi # Action to remove the symlinks if [ "$ACTION" = "remove" ] ; then case "${PRODUCT}" in 1bc7*) # Telit HE910 3g modules product id prefix # Module Telit HE910-* connected to minipciexpress slot MAIN if [ "${DEVICENAME}" = "1-1.3:1.0" ] ; then rm /dev/ttyMODULO1_DIAL logger -t Hotplug "Symlink /dev/ttyMODULO1_DIAL removed" elif [ "${DEVICENAME}" = "1-1.3:1.6" ] ; then rm /dev/ttyMODULO1_DATA logger -t Hotplug "Symlink /dev/ttyMODULO1_DATA removed" # Module Telit HE910-* connected to minipciexpress slot SECONDARY elif [ "${DEVICENAME}" = "1-1.2:1.0" ] ; then rm /dev/ttyMODULO2_DIAL logger -t Hotplug "Symlink /dev/ttyMODULO2_DIAL removed" elif [ "${DEVICENAME}" = "1-1.2:1.6" ] ; then rm /dev/ttyMODULO2_DATA logger -t Hotplug "Symlink /dev/ttyMODULO2_DATA removed" fi ;; esac fi EOF