Hotplug
Procd (ініціалізаційна система та демон керування процесами) виконує скрипти, розміщені в каталозі /etc/hotplug.d, коли відбуваються певні події — наприклад, коли інтерфейс підключається або відключається, коли виявлено новий накопичувач, або коли натиснута кнопка.
Це може бути дуже корисним для PPPoE-з’єднання, в умовах нестабільної мережі або для використання апаратних кнопок.
Ця функціональність емулює або розширює можливості, які раніше реалізовувалися за допомогою застарілого пакету Hotplug2.
Як це працює
У каталозі /etc/hotplug.d ви знайдете підкаталоги block, iface, net та ntp.
Коли виникає тригерна подія, Procd виконує всі скрипти в відповідному каталозі цієї події, у алфавітному порядку. Саме тому більшість скриптів мають числовий префікс.
| Каталог | Опис |
|---|---|
| block | Події з блочними пристроями: пристрій підключено/відключено |
| button | Події кнопок: за замовчуванням не створюється, див. /etc/rc.button |
| dhcp | Події, пов’язані з DHCP |
| dsl | Події модему DSL |
| firewall | Події, пов’язані з фаєрволом |
| iface | Події інтерфейсів: підключення/відключення LAN/WAN тощо |
| neigh | Виявлення сусідів (Neighbor Discovery) |
| net | Події, пов’язані з мережею |
| ntp | Події синхронізації часу: зміна часу, зміна рівня stratum NTP сервера |
| tftp | Події, пов’язані з TFTP |
| tty | Події з TTY-пристроями, включно з модемами WWAN, що напряму вказують на інтерфейс TTY |
| usb | USB-пристрої, наприклад, 3G-модеми та tty* |
| usbmisc | Спеціальні USB-периферійні пристрої, такі як принтери (у цьому підсистемі доступно менше змінних) |
Див. також:
Кнопки, звукові пристрої, послідовні та USB-послідовні адаптери
Використання / Виправлення проблем
Просто розмістіть ваші скрипти в потрібному підкаталозі hotplug.d. Якщо каталогу не існує — створіть його.
Procd передає велику кількість інформації до скриптів у /etc/hotplug.d, зазвичай у вигляді змінних середовища.
Якщо ви хочете побачити, які саме змінні передаються, створіть такий скрипт:
cat << "EOF" > /etc/hotplug.d/iface/00-logger logger -t hotplug $(env) EOF
Потім виконайте подію, пов’язану з цим каталогом, і перегляньте змінні середовища через:
logread -e hotplug
Класи подій / Каталоги
net
Для скриптів у каталозі net, доступні такі (релевантні) змінні середовища:
| Назва змінної | Опис |
|---|---|
| ACTION | Значення “add” або “remove” (додавання або видалення пристрою) |
| DEVICENAME | Імена налаштованих інтерфейсів (наприклад, br-lan, wlan0, phy1-ap0) |
| PATH | Повний шлях |
| DEVPATH | Повний шлях до пристрою (наприклад, “/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 | Тип пристрою, до якого належить DEVICENAME (наприклад, br-lan, phy1-ap0) |
| INTERFACE | Налаштовані інтерфейси, подібно до DEVTYPE |
| SEQNUM | Порядковий номер події (ціле число) |
| SUBSYSTEM | Завжди дорівнює “net” |
| IFINDEX | Індекс інтерфейсу, можна переглянути через ifconfig |
block
Для скриптів у каталозі block, доступні такі (релевантні) змінні середовища:
| Назва змінної | Опис |
|---|---|
| ACTION | Для звичайного пристрою (наприклад, sda) значення буде “add” або “remove”. Може бути “change” для зашифрованого пристрою (dm-crypt, наприклад, dm-0) |
| DEVICENAME | Як правило, те саме, що і DEVNAME |
| DEVNAME | Назва пристрою або розділу (наприклад, підключення накопичувача викликає події з sda і потім з sda1) |
| DEVPATH | Повний шлях до пристрою (наприклад, “/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 | Тип імені пристрою, наприклад, “partition” для розділу або “disk” при видаленні |
| MAJOR | Основний номер пристрою |
| MINOR | Додатковий номер пристрою |
| SEQNUM | Порядковий номер події (число) |
| SUBSYSTEM | Завжди дорівнює “block” |
dsl
Для скриптів у каталозі dsl доступні наступні (релевантні) змінні середовища:
| Назва змінної | Опис |
|---|---|
| DSL_NOTIFICATION_TYPE | DSL_STATUS, DSL_INTERFACE_STATUS, DSL_DATARATE_STATUS_US, DSL_DATARATE_STATUS_DS |
| DSL_LINE_NUMBER | 0, 1 * |
Коли DSL_NOTIFICATION_TYPE дорівнює DSL_STATUS, встановлюються наступні змінні:
| Назва змінної | Опис |
|---|---|
| DSL_XTU_STATUS | ADSL, VDSL |
| DSL_TC_LAYER_STATUS | ATM, EFM |
| DSL_EFM_TC_CONFIG_US | NORMAL, PRE_EMPTION, UNKNOWN |
| DSL_EFM_TC_CONFIG_DS | NORMAL, UNKNOWN |
Коли DSL_NOTIFICATION_TYPE дорівнює DSL_INTERFACE_STATUS:
| Назва змінної | Опис |
|---|---|
| DSL_INTERFACE_STATUS | DOWN, READY, HANDSHAKE, TRAINING, UP |
| DSL_BONDING_STATUS | INACTIVE, ACTIVE * |
Коли DSL_NOTIFICATION_TYPE дорівнює DSL_DATARATE_STATUS_US:
| Назва змінної | Опис |
|---|---|
| DSL_DATARATE_US_BC0 | Швидкість висхідної передачі в біт/с для каналу 0 |
| DSL_DATARATE_US_BC1 | Швидкість висхідної передачі в біт/с для каналу 1 * |
Коли DSL_NOTIFICATION_TYPE дорівнює DSL_DATARATE_STATUS_DS:
| Назва змінної | Опис |
|---|---|
| DSL_DATARATE_DS_BC0 | Швидкість низхідної передачі в біт/с для каналу 0 |
| DSL_DATARATE_DS_BC1 | Швидкість низхідної передачі в біт/с для каналу 1 * |
Примітка: Змінні, позначені зірочкою (*), доступні лише якщо підтримка агрегації каналів (channel bonding) скомпільована у систему.
iface
Для скриптів у каталозі iface доступні наступні змінні середовища:
| Назва змінної | Опис |
|---|---|
| ACTION | ifdown, ifup, ifup-failed, ifupdate, free, reload, iflink, create |
| INTERFACE | Назва логічного інтерфейсу, який піднято або опущено (наприклад, wan, lan) |
| DEVICE | Назва фізичного пристрою, який змінив стан (наприклад, eth0, br-lan, pppoe-wan) |
Менш очевидні значення ACTION:
| Значення | Опис |
|---|---|
| ifup-failed | Подія ifdown сталася під час спроби підняти інтерфейс |
| free | Інтерфейс видалено (зворотне до create) |
| iflink | Інтерфейс повідомив про наявність носія (carrier) |
Якщо ACTION — це ifupdate, можуть бути встановлені такі змінні:
| Назва змінної | Опис |
|---|---|
| IFUPDATE_ADDRESSES | 1, якщо змінилася IP-адреса |
| IFUPDATE_ROUTES | 1, якщо змінився маршрут |
| IFUPDATE_PREFIXES | 1, якщо змінився префікс |
| IFUPDATE_DATA | 1, якщо виконано ubus call network.interface.$INTERFACE set_data ... або подібне |
ntp
Змінні на основі busybox ntpd:
| Назва змінної | Опис |
|---|---|
| ACTION | step, stratum, unsync, periodic |
| freq_drift_ppm | Зсув частоти |
| offset | Корекція часу |
| stratum | Рівень якості часу (відстань до атомного годинника) |
| poll_interval | Інтервал опитування |
Навіть без синхронізації з NTP ви будете отримувати подію periodic з stratum=16 приблизно кожні 11 хвилин.
tty
| Назва змінної | Опис |
|---|---|
| ACTION | add, remove, bind, unbind |
| DEVICENAME | Наприклад, ttyUSB2 |
| DEVNAME | Тільки для bind/unbind, напр. ttyUSB2 |
| DEVPATH | Шлях до пристрою, напр. /devices/platform/ahb/.../ttyUSB2 |
| SEQNUM | Номер події з моменту запуску системи |
| SUBSYSTEM | Тип пристрою, напр. usb-serial |
| MAJOR | Основний номер пристрою |
| MINOR | Додатковий номер пристрою |
usb
| Назва змінної | Опис |
|---|---|
| ACTION | add, remove, bind, unbind |
| DEVICENAME | Напр., 1-1 |
| DEVNAME | Напр., bus/usb/001/002 |
| DEVNUM | Напр., 002 |
| DEVPATH | Напр., /devices/platform/ehci-platform/usb1/1-1 |
| DEVTYPE | Тип пристрою, напр., usb_device |
| TYPE | Класифікація пристрою, напр., 9/0/1 |
| PRODUCT | Коди виробника/пристрою/версії, напр. 424/2640/0 (див. lsusb) |
| SEQNUM | Номер події з моменту запуску |
| BUSNUM | Номер шини USB, напр., 001 |
| MAJOR | Основний номер пристрою |
| MINOR | Додатковий номер пристрою |
usbmisc
| Назва змінної | Опис |
|---|---|
| ACTION | add, remove |
| DEVNAME | Напр., bus/usb/001/002 |
| DEVPATH | Напр., /devices/platform/ehci-platform/usb1/1-1 |
| DEVICENAME | Напр., 1-1 |
| SEQNUM | Номер події з моменту запуску |
| MAJOR | Основний номер пристрою |
| MINOR | Додатковий номер пристрою |
Приклади
cat << "EOF" > /etc/hotplug.d/iface/99-my-action [ "${ACTION}" = "ifup" ] && { logger -t hotplug "Device: ${DEVICE} / Action: ${ACTION}" } EOF
Щоразу, коли інтерфейс піднімається, буде виконано блок if.
Символьне посилання замість перейменування пристрою
Ще один скрипт, який створює символічне посилання (symlink) замість перейменування пристрою:
cat << "EOF" > /etc/hotplug.d/usb/20-cp210x CP210_PRODID='10c4/ea60/100' SYMLINK="my_link" set -eu if [ "${DEVTYPE:-}" = 'usb_interface' ] && \ [ "${PRODUCT:-}" = "${CP210_PRODID}" ]; then if [ "${ACTION:-}" = 'bind' ]; then if [ -L "/dev/${SYMLINK}" ]; then logger -t hotplug "Symlink '/dev/${SYMLINK}' already exists" exit 0 fi DEVICE_NAME="$(find /sys${DEVPATH:-} -maxdepth 1 -type d -iname 'ttyUSB*' -exec basename {} \;)" if [ -z "${DEVICE_NAME}" ]; then logger -t hotplug 'Warning: DEVICE_NAME is empty' exit 0 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 if [ "${ACTION:-}" = 'unbind' ]; then rm "/dev/${SYMLINK}" logger -t hotplug "Symlink '/dev/${SYMLINK}' removed" fi fi EOF
Запуск ser2net при події hotplug
Аналогічно, також можливо запускати ser2net для надання доступу до послідовного порту віддалено через мережу.
cat << "EOF" > /etc/hotplug.d/usb/20-cp210x-ser2net CP210_PRODID='10c4/ea60/100' SER2NET_PID='/var/run/ser2net.pid' SER2NET_PORT=18888 SER2NET_BAUDRATE=115200 SER2NET_FLOWRATE='software' SER2NET_RADIO='znp' SER2NET_OPTS="${SER2NET_BAUDRATE} NONE 1STOPBIT 8DATABITS -RTSCTS -LOCAL NOBREAK" set -eu if [ "${DEVTYPE:-}" = 'usb_interface' ] && \ [ "${PRODUCT:-}" = "${CP210_PRODID}" ]; then if [ "${ACTION:-}" = 'bind' ]; then if [ -s "${SER2NET_PID}" ]; then logger -t hotplug "Warning: ser2net already running as pid: $(cat "${SER2NET_PID}") via ${SER2NET_PID}" exit 0 fi DEVICE_NAME="$(find "/sys${DEVPATH:-}" -maxdepth 1 -type d -iname 'ttyUSB*' -exec basename {} \;)" if [ -z "${DEVICE_NAME}" ]; then logger -t hotplug 'Warning: DEVICE_NAME is empty' exit 0 fi logger -t hotplug "Device name of cp210 is '${DEVICE_NAME}'" ser2net -C "${SER2NET_PORT}:raw:100:/dev/${DEVICE_NAME}:${SER2NET_OPTS}" -P "${SER2NET_PID}" if [ -d '/etc/avahi/services' ]; then { printf '<service-group>\n\n' printf ' <name replace-wildcards="yes">%%h</name>\n\n' printf ' <service>\n' printf ' <type>_ser2net_zigbee-gateway._tcp</type>\n' printf ' <port>%d</port>\n' "${SER2NET_PORT}" printf ' <txt-record>baudrate=%d</txt-record>\n' "${SER2NET_BAUDRATE:-115200}" printf ' <txt-record>flow-control=%s</txt-record>\n' "${SER2NET_FLOWRATE:-software}" printf ' <txt-record>radio=%s</txt-record>\n' "${SER2NET_RADIO:-znp}" printf ' </service>\n\n' printf '</service-group>\n' } > '/etc/avahi/services/ser2net.service' fi logger -t hotplug "Started ser2net on '/dev/${DEVICE_NAME}'" fi if [ "${ACTION:-}" = 'unbind' ]; then logger -t hotplug "Attempting to stop ser2net with pid: $(cat "${SER2NET_PID}")" kill -3 "$(cat "${SER2NET_PID}")" if [ -s '/etc/avahi/services/ser2net.service' ]; then rm '/etc/avahi/services/ser2net.service' fi if [ ! -s "${SER2NET_PID}" ]; then logger -t hotplug 'Failed' exit 0 fi fi fi EOF
Примітка: Наведений вище приклад також демонструє інтеграцію з Avahi, що забезпечує автоматичне виявлення послідовного порту в мережі. У цьому прикладі до послідовного порту підключено Zigbee-пристрій.
Скрипт для визначення, чи підключено USB-пристрій Bluetooth
cat << "EOF" > /etc/hotplug.d/usb/20-bt_test BT_PRODID="a12/1/" BT_PRODID_HOT="${PRODUCT::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
Автоматичний запуск mjpg-streamer при підключенні USB-камери
cat << "EOF" > /etc/hotplug.d/usb/20-mjpg_start case "${ACTION}" in add) # start process service mjpg-streamer start ;; remove) # stop process service mjpg-streamer stop ;; esac EOF
Кастомний скрипт автоматичного монтування для 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
Ви могли помітити, що udev та eudev були видалені у випуску OpenWrt 18.0.*.
Не хвилюйтеся — все ще можна змусити все працювати.
Замість них можна використовувати скрипти hotplug як coldplug. Зверніть увагу на змінну оточення ACTION: при завантаженні системи виконуються дії типу bind. Тому просто додайте цю перевірку у ваш скрипт hotplug і він спрацює належним чином.
У моєму випадку я використав таке:
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" 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
Логування змін статусу DSL
Якщо у вас є DSL-модем, ви можете увімкнути журналювання змін статусу DSL та швидкостей передачі даних.
Це може бути корисним, якщо на ваш DSL-зв’язок впливають події Seamless Rate Adaptation (SRA) або Dynamic Line Management (DLM); у разі подій SRA будуть фіксуватись зміни швидкості передачі даних, а у випадку подій DLM — зміни статусу з’єднання, оскільки модем змушений перетренуватися, після чого будуть зафіксовані нові швидкості передачі даних.
cat << "EOF" > /etc/hotplug.d/dsl/20-dsl_status case "${DSL_NOTIFICATION_TYPE}" in (DSL_INTERFACE_STATUS) logger -p daemon.notice -t dsl-notify "${DSL_XTU_STATUS} link status: ${DSL_INTERFACE_STATUS}" ;; (DSL_DATARATE_STATUS_US) logger -p daemon.notice -t dsl-notify "DSL upstream actual data rate: ${DSL_DATARATE_US_BC0}" ;; (DSL_DATARATE_STATUS_DS) logger -p daemon.notice -t dsl-notify "DSL downstream actual data rate: ${DSL_DATARATE_DS_BC0}" ;; esac EOF
Цей скрипт передбачає, що підтримка об'єднання каналів (channel bonding) не скомпільована в DSL-модулі.
Використання бездротового USB-адаптера
Перезапуск Wi-Fi при підключенні бездротового USB-адаптера.
mkdir -p /etc/hotplug.d/usb cat << "EOF" > /etc/hotplug.d/usb/20-rtl8188su if [ "${PRODUCT}" = "bda/8171/200" ] \ && [ "${ACTION}" = "add" ] then wifi fi EOF
“Наведений вище код спрацьовує для такого пристрою.
# lsusb -v idVendor 0x0bda Realtek Semiconductor Corp. idProduct 0x8171 RTL8188SU 802.11n WLAN Adapter bcdDevice 2.00
Отримання конкретної IP-адреси
Припускається, що ваш провайдер надає динамічну IP-адресу. Повторюйте підключення, доки не отримаєте адресу, що відповідає певному регулярному виразу.
Перед кожною новою спробою зробіть паузу 10 секунд.
Налаштуйте додаткову обробку подій hotplug, щоб запускати цей скрипт після підключення WAN-інтерфейсу.
mkdir -p /etc/hotplug.d/online cat << "EOF" > /etc/hotplug.d/online/10-wan-ipaddr . /lib/functions/network.sh network_flush_cache network_find_wan WAN_IF network_get_ipaddr WAN_ADDR "${WAN_IF}" if [ "${WAN_IF}" != "${INTERFACE}" ] then exit 0 fi case ${WAN_ADDR} in (??.???.*) exit 0 ;; esac sleep 10 ifup ${INTERFACE} EOF
Перейменування інтерфейсів за MAC-адресою
Припускається, що заздалегідь налаштовані інтерфейси — wana та wanb.
Для встановлення постійних (детермінованих) імен мережевих інтерфейсів на основі MAC-адреси, виконайте наступні дії.
cat << "EOF" > /etc/hotplug.d/iface/00-dev-rename dev_rename() { local DEV_CONF="${1}" local DEV_MAC DEV_NAME DEV_ONAME config_get DEV_MAC "${DEV_CONF}" mac config_get DEV_NAME "${DEV_CONF}" name DEV_ONAME="$(grep -l -e "${DEV_MAC}" \ $(find /sys/class/net/*/device/uevent \ | sed -e "s|/device/uevent$|/address|") \ | awk -F '/' '{print $5}')" if [ -n "${DEV_MAC}" ] \ && [ "${DEV_ONAME}" != "${DEV_NAME}" ] then ip link set "${DEV_ONAME}" name "${DEV_NAME}" fi } . /lib/functions.sh config_load network config_foreach dev_rename device EOF while read -r DEV_NAME DEV_MAC do uci set network.${DEV_NAME}.device="${DEV_NAME}" uci set network.${DEV_NAME}6.device="${DEV_NAME}" uci -q delete network.${DEV_NAME}_dev uci set network.${DEV_NAME}_dev="device" uci set network.${DEV_NAME}_dev.mac="${DEV_MAC}" uci set network.${DEV_NAME}_dev.name="${DEV_NAME}" done << EOI wana 11:22:33:44:55:66 wanb aa:bb:cc:dd:ee:ff EOI uci commit network service network restart
Створення детермінованих посилань на USB-пристрої з послідовним інтерфейсом
Результат: символічні посилання будуть створені в каталогах /dev/serial/by-id та /dev/serial/by-path.
Імена посилань матимуть структуру, яка максимально подібна до тієї, яку створює udev у більшості дистрибутивів Linux, але не є повністю ідентичною.
set -o pipefail [ "${ACTION}" = "bind" -o "${ACTION}" = "unbind" ] || exit 0 [ "${SUBSYSTEM}" = "usb-serial" ] || exit 0 [ -n "${DEVICENAME}" -a -n "${DEVPATH}" ] || exit 1 if [ "${ACTION}" = "bind" ]; then subsystem="$(basename $(readlink /sys${DEVPATH}/../subsystem))" [ "$subsystem" = "usb" ] || exit 0 replace_whitespace="s/^[ \t]*|[ \t]*$//g; s/[ \t]+/_/g" manufacturer="$(cat /sys${DEVPATH}/../../manufacturer | sed -E "${replace_whitespace}")" || manufacturer="$(cat /sys${DEVPATH}/../../idVendor)" product="$(cat /sys${DEVPATH}/../../product | sed -E "${replace_whitespace}")" || product="$(cat /sys${DEVPATH}/../../idProduct)" serial="$(cat /sys${DEVPATH}/../../serial | sed -E "${replace_whitespace}")" interface="$(cat /sys${DEVPATH}/../bInterfaceNumber)" port="$(cat /sys${DEVPATH}/port_number)" replace_chars="s/[^0-9A-Za-z#+.:=@-]/_/g" id_link=$(echo "${subsystem}"-"${manufacturer}"_"${product}${serial:+_}${serial}"-if"${interface}${port:+-port}${port}" | sed "${replace_chars}") path_link=$(echo "${DEVPATH}${port:+-port}${port}" | sed "s%/devices/%%; s%/${DEVICENAME}%%g; ${replace_chars}") mkdir -p /dev/serial/by-id /dev/serial/by-path ln -sf "/dev/${DEVICENAME}" "/dev/serial/by-id/${id_link}" ln -sf "/dev/${DEVICENAME}" "/dev/serial/by-path/${path_link}" elif [ "${ACTION}" = "unbind" ]; then for link in $(find /dev/serial -type l); do [ -L ${link} -a "$(readlink ${link})" = "/dev/$DEVICENAME" ] && rm ${link} done fi