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.

In the /etc/hotplug.d directory you will find some directories block, iface, net and ntp.

When the trigger event fires, Procd will execute all scripts in that trigger's directory, in alphabetical order. Which is why most scripts in there use a numeric prefix.

Directory Description
block Block device events: device connected/disconnected
button Buttons: not created by default, see /etc/rc.button instead
dhcp DHCP-related events
firewall Firewall-related events
iface Interface events: LAN/WAN/etc. is connected/disconnected
neigh Neighbor discovery
net Network-related events
ntp Time sync events: time step, time server stratum change
tftp TFTP-related events
usb USB devices like 3g-modem and tty*

See also:

Simply place your script(s) into the right hotplug.d directory, if there is none just create the right one.

Procd exposes a wealth of info when executes your scripts in /etc/hotplug.d, usually as environmental variables.

If you want to see what environmental variables it is providing, make a script like this:

cat << "EOF" > /etc/hotplug.d/iface/00-logger
logger -t hotplug $(env)

Then trigger the event connected to that directory, and then you can see what envs were passed by reading the output:

logread -e hotplug

For scripts in block directory, these are the (relevant) environmental variables

Variable name Description
ACTION For normal device (e.g: sda) it is either “add” or “remove”. Can be “change” if the device is dm-crypt (e.g: dm-0)
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”

There are three main environment variables that are passed to each iface hotplug script:

Variable name Description
ACTION “ifup”, “ifdown”, “ifupdate”
INTERFACE Name of the logical 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”)
IFUPDATE_ADDRESSES “1” if address changed
IFUPDATE_PREFIXES “1” if prefix updated FIXME what constitutes an update?
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.

Variable name description
ACTION add, remove as above
DEVNAME eg, “bus/usb/001/002”
DEVPATH eg, “/devices/platform/ehci-platform/usb1/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
cat << "EOF" > /etc/hotplug.d/iface/99-my-action
[ "${ACTION}" = "ifup" ] && {
    logger -t hotplug "Device: ${DEVICE} / Action: ${ACTION}"

Every time an interface goes up then the if/fi statement will be executed.

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
mkdir -p /etc/hotplug.d/usb
cat << "EOF" > /etc/hotplug.d/usb/20-rtl8188su
BINARY="/sbin/wifi up"
if [ "${PRODUCT}" = "${RTL8188SU_PRODID}" ]; then
    if [ "${ACTION}" = "add" ]; then

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
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"
          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"
if [ "${PRODUCT}" = "${CP210_PRODID}" ]; then
    if [ "${ACTION}" = "remove" ]; then
        rm /dev/${SYMLINK}
        logger -t hotplug "Symlink /dev/${SYMLINK} removed"
cat << "EOF" > /etc/hotplug.d/usb/20-bt_test
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"
            logger -t hotplug "bluetooth device has changed"
    if [ "${ACTION}" = "remove" ]; then
        logger -t hotplug "bluetooth device has been removed!"
    logger -t hotplug "USB device is not bluetooth"
cat << "EOF" > /etc/hotplug.d/usb/20-mjpg_start
case "${ACTION}" in
            # start process
        /etc/init.d/mjpg-streamer start
            # stop process
        /etc/init.d/mjpg-streamer stop
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
    case "${detected_uuid}" in
    # if we have a known UUID we have a mountpoint so we can mount it
    if [ "${mountpoint}" != "" ]; then 
        mount /dev/${DEVICENAME} ${mountpoint}
# unmounting happens automatically at device disconnection anyway so no logic for that

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. You can use hotplug scripts as coldplug. Pay attention at the ACTION environment variable, at the boot are executed 'bind' actions. So, just add this option to hotplug run accordinly. In my case I used this:

mkdir -p /etc/hotplug.d/usb
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"
# 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"

A simple method to configure persistent interface names by MAC address.

uci set network.wan1.ifname="wan1"
uci set network.wan2.ifname="wan2"
uci -q delete network.wan1_dev
uci set network.wan1_dev="device"
uci set"wan1"
uci set network.wan1_dev.mac="11:22:33:44:55:66"
uci -q delete network.wan2_dev
uci set network.wan2_dev="device"
uci set"wan2"
uci set network.wan2_dev.mac="aa:bb:cc:dd:ee:ff"
uci commit network
cat << "EOF" > /etc/rc.local
NET_DEVS="$(ls /sys/class/net/*/device/uevent \
    | awk -F '/' '{print $5}')"
for NET_DEV0 in ${NET_DEVS}
    NET_MAC0="$(cat /sys/class/net/"${NET_DEV0}"/address)"
    while uci -q get network.@device["${I}"] > /dev/null
        NET_DEV="$(uci -q get network.@device["${I}"].name)"
        NET_MAC="$(uci -q get network.@device["${I}"].mac)"
        if [ -n "${NET_DEV}" -a -n "${NET_MAC}" ] \
        && [ "${NET_MAC}" = "${NET_MAC0}" ]
            ip link set "${NET_DEV0}" down
            ip link set "${NET_DEV0}" name "${NET_DEV}"
/etc/init.d/network reload
sh /etc/rc.local
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2021/07/28 22:51
  • by vgaetera