Как писать правила для UDEV

by Daniel Drake (dsd)

Version 0.74

 

Перевод: Аркадий Иванов (arc@ikir.ru)

 

Последнюю версию этого документа можно найти по адресу:

http://www.reactivated.net/writing_udev_rules.html

 

Содержание

Введение

 

Об этом документе

udev предназначен для Linux ядер 2.6 и последующих за ними. Обеспечивает управление из пространства пользователя динамическим каталогом /dev с устойчивым наименованием  устройств (устойчивый означает, что наименование не меняется после перезагрузки системы или переподключения устройства). Предыдущее решение для /dev , так называемое devfs, сегодня устарело, и похоже, что udev всех победило. udev против devfs - это весьма болезненная область споров и вам следует прочитать  этот документ прежде чем проводить сравнения.

За прошедшие годы поменялось то, для чего вы может быть использовали правила udev, так же как и гибкость самих правил. На современных системах udev обеспечивает устойчивое наименование некоторых типов устройств прямо из коробки, исключая необходимость создания собственных правил для таких устройств. Однако, некоторым пользователям всё ещё требуется некоторый дополнительный уровень собственной донастройки.

Этот документ предполагает, что udev установлен и запущен в конфигурации, которая предлагается по умолчанию. Это обычно обеспечивает ваш дистрибутив Linux.

Этот документ не описывает каждую деталь того, как писать правила, целью является дать представление об основных концепциях. Все уточнения можно найти на страницах "man udev".


Этот документ даёт множестов примеров (многие из которых полностью придуманы) для иллюстрации идеи и концепций. В комментариях к строкам правил объясняется не весь синтаксис, удостоверьтесь, что вы изучили до полного понимания сами правила приведённые в примерах.

 

История

  • April 5th 2008 v0.74: Typo fixes.
  • December 3rd 2007 v0.73: Update for new udev versions, and some miscellaneous improvements.
  • October 2nd 2006 v0.72: Fixed a typo in one of the example rules.
  • June 10th 2006 v0.71: Misc changes based on recent feedback - thanks!
  • June 3rd 2006 v0.7: Complete rework, to be more suited for the modern-day udev.
  • May 9th 2005 v0.6: Misc updates, including information about udevinfo, groups and permissions, logging, and udevtest.
  • June 20th 2004 v0.55: Added info on multiple symlinks, and some minor changes/updates.
  • April 26th 2004 v0.54: Added some Debian info. Minor corrections. Re-reverted information about what to call your rule file. Added info about naming network interfaces.
  • April 15th 2004 v0.53: Minor corrections. Added info about NAME{all_partitions}. Added info about other udevinfo tricks.
  • April 14th 2004 v0.52: Reverted to suggesting using "udev.rules" until the udev defaults allow for other files. Minor work.
  • April 6th 2004 v0.51: I now write suggest users to use their own "local.rules" file rather than prepending "udev.rules".
  • April 3rd 2004 v0.5: Minor cleanups and preparations for possible inclusion in the udev distribution.
  • March 20th 2004 v0.4: General improvements, clarifications, and cleanups. Added more information about writing rules for usb-storage.
  • February 23rd 2004 v0.3: Rewrote some parts to emphasise how sysfs naming works, and how it can be matched. Updated rule-writing parts to represent udev 018s new SYSFS{filename} naming scheme. Improved sectioning, and clarified many points. Added info about KDE.
  • February 18th 2004 v0.2: Fixed a small omission in an example. Updated section on identifying mass-storage devices. Updated section on nvidia.
  • February 15th 2004 v0.1: Initial publication.

Концепции

 

Терминология: devfs, sysfs, nodes, etc.

Это всего лишь общее введение. Может быть не во всех частях полностью точное.

На обычных Linux-системах каталог /dev содержит похожие на файлы узлы(nodes) устройств, которые ссылаются на конкретные устройства в системе. Каждый узел указывает на часть системы (на устройство), которое может присутствовать, а может и нет. Приложения из пространства пользователя могут использовать эти узлы устройства для взаимодействия с оборудованием системы. Например, X сервер слушает /dev/input/mice и может соотнести физические перемещения мыши с позиционированием мышиного курсора на экране.

Исходно каталоги /dev наполнялись узлами каждого устройства, которое могло бы стать частью системы. По этой причине каталоги /dev обычно были очень большими. devfs  пошла дальше, чтобы дать более управляемую систему (отличием было то, что она наполняла /dev устройствами, которые подключены к системе),  а также и некоторую дополнительную функциональность. Однако эта система доказала, что в ней есть не очень-то легко решаемые проблемы.

udev - это новый способ управления каталогами /dev, созданный для того, чтобы избавиться от некоторых проблем предыдущих решений для /dev, и чтобы обеспечить гибкость в будущем развитии. Для создания узлов устройств и их имён в /dev, которые соответствовали ли бы устройствам, присутствующим в системе, udev полагается на соответствие между информацией, которая присутствует в sysfs, и правилами, заданных пользователем. Цель этого документа - детализировать процесс написания правил. Это одна из задач, относящаяся к udev, которую быть может придётся выполнять самому пользователю.

sysfs это новая файловая система в ядрах 2.6. Она управляется ядром и экспортирует основную информацию об устройствах, которые в текущий момен подключены в вашу систему. udev может использовать эту информацию для создания узлов устройств, соответствующих вашему оборудованию. sysfs монтирована в каталог /sys и доступна для просмотра. Вы можете поизучать некоторые из хранящихся там файлов до того, как вплотную приступить к udev. В этом документе термины /sys и sysfs полностью взаимозаменяемы.

 

Зачем?

Правила udev очень гибкие и очень мощные. Вот несколько вещей, которые вы можете достичь используя правила udev:

  • Сменить имя по умолчанию для узла устройства на что-нибудь другое.
  • Задать альтернативное/устойчивое имя для узла устройства созданием сиволического линка на узел, который был создан по умолчанию
  • Присвоить имя узлу устройства на основе вывода, получаемого от программы.
  • Сменить права и владельца узла устройства
  • Запустить скрипт в момент когда узел устройства создаётся или удаляется (обычно в моменты подключения или отключения устройства)
  • Переименование сетевых интерфейсов

Написанием правил не решить проблему, когда для вашего конкретного устройства нет узла этого устройства.

Даже если нет соответствующего правила, udev создаст узел устройства с именем по умолчанию, которое будет получено от ядра.

Иметь устойчивые наименования для узлов устройст имеет свои преимущества. Предположим, что у вас есть 2 USB устройства-накопителя: цифровая камера и USB-флэш диск. Эти устройства обычно получают узлы с именами /dev/sda и /dev/sdb, но наименование зависит от того, в каком порядке вы они были подключены в систему. Это может стать проблемой для некоторых пользователей, которые очень сильно выиграют, если устройства будут всегда именоваться устойчиво, например /dev/camera и /dev/flashdisk.

 

Встроенные устойчивые схемы наименований

udev обеспечивает устойчивое наименование для некоторых типов устройств прямо из коробки. Это очень полезное свойство и во многих случаях это означает, что ваше путешествие прямо здесь и заканчивается: вам вообще не требуется писать хоть какое-либо правило.

udev обеспечивает прямо-из-коробки устойчивое наименование устройств хранения данных в каталоге /dev/disk. Чтобы увидеть устойчивые имена, которые были сделаны для ваших устройств хранения данных, используйте следующую команду:

# ls -lR /dev/disk

Это работает для всех типов устройст хранения данных. Например, udev создал /dev/disk/by-id/scsi-SATA_ST3120827AS_4MS1NDXZ-part3, что является устойчивым именем символического линка к моему корневому разделу. udev создаёт /dev/disk/by-id/usb-Prolific_Technology_Inc._USB_Mass_Storage_Device-part1, когда я втыкаю свой USB флэш диск. И это также устойчивое имя.

Написание правил

 

Файлы правил и семантики

Чтобы дать имя устройству и быть может произвести дополнительные действия, udev читает последовательно файлы с правилами. Эти файлы содержатся в каталоге /etc/udev/rules.d, и должны иметь суффикс .rules .

Правила по умолчанию для udev хранятся в /etc/udev/rules.d/50-udev.rules. Вам может показаться интерестным взглянуть на этот файл - он содержит несколько примеров, и затем несколько правил по умолчанию, которые структурируют содержимое каталога /dev в стиле devfs. Однако вам не следует записывать правила напрямую в этот файл.

Файлы в /etc/udev/rules.d/ обрабатываются в алфавитном порядке, и в ряде случаем порядок, в котором разбираются правила может быть важным. Обычно вы хотите, чтобы ваши собственные правила обрабатывались до правил по умолчанию, так что, я советую создать файл /etc/udev/rules.d/10-local.rules и вписывать все свои правила в него.

В файлах правил строки, начинающиеся с "#" трактуются как комментарии. Каждая непустая строка - это правило. Правила не могут занимать более одной строки.

Одному устройству может соответствовать более одного правила. Удобство состоит в том, что, например, мы можем написать два правила, соответствующих одному и тому же устройству, и каждое правило обеспечит своё собственное альтернативное имя для устройства. Оба альтернативных имени будут созданы, даже если правила находятся в разных файлах. Важно понять, что udev не заканчивает обработку после нахождения подходящего, а продолжит просмотр и применит каждое правило,  которое подходит.

 

Синтаксис правил

Каждое правило состоит из серии пар "ключ"-"значение", которые разделены запятой. ключи соответствия - это условия, которые используются для идентификации устройства, на которое действует правило. Когда все ключи соответствия правила соответствуют обрабатываемому устройству, правило применяется и производится действие, которое задано ключамиприсвоения. Каждое правило состоит как минимум из одного ключа соответствия и одного присвоения.

Вот пример правила для иллюстрации вышесказанного:

KERNEL=="hdb", NAME="my_spare_disk"

Приведённое правило содержит один ключ соответствия (KERNEL) и один присвоения (NAME). Семантика этих ключей и их свойства будут обсуждены в деталях позже. Важно заметить, что ключ соответствия связан со своим значением оператором сравнения на равенство (==), в то время как присваивающий ключ связан со своим значением оператором присваивания (=).

Знайте, что udev не поддерживает никаких продолжений строк. Не вставляйте переводы строк в ваши правила,  так как это заставит udev рассматривать ваше одно правило как несколько правил и это точно не сработает так, как вы хотели бы.

 

Базовые правила

udev даёт несколько ключей соответствия, которые можно использовать при написании правил, и по которым очень точно устанавливается соответствие с устройством. Некоторые из наиболее общих ключей представлены ниже, остальные будут представлены в этом документе позднее.  Чтобы увидеть полный список, читайте страницы манов по udev.

  • KERNEL - соответствие с именем устройства в ядре
  • SUBSYSTEM - соответствие с подсистемой устройства
  • DRIVER - соответствие с именем драйвера, обслуживающего устройство

После того, как вы использовали список ключей соответствия, чтобы точно определить устройство, udev позволяет вам удобно управлять тем, что делать далее с помощью набора присваивающих ключей. Чтобы увидеть полный список возможных присваивающих ключей, читайте маны udev.  Наиболее общие присваивающие ключи представлены ниже. Остальные будут представлены в этом документе позднее.

  • NAME - имя, которое будет использовано для узла устройства
  • SYMLINK -  список символических линков, которые действуют как альтернативные имена для узла устройства

Как можно догадаться из вышесказанного, udev создаёт один реальный узел для одного устройства. Есил вы хотите иметь ещё альтернативные имена для этого узла, используйте возможность создания символических линков. С помощью SYMLINK,  вы действительно поддерживаете список символических линков, каждый из которых указывает на реальный узел устройства. Для манипуляций этими линками используйте другой оператор, которым можно добавлять что-то в списки: +=.  Вы можете добавлять в любом правиле несколько символических линков в список, разделяя каждое добавление пробелом.

KERNEL=="hdb", NAME="my_spare_disk"

Вышенаписанное правило гласит: при обнаружении устройства, которое имеет в ядре имя hdb,  вместо того, чтобы назвать его hdb,  дать имя узлу устройства my_spare_disk. В результате узел устройства будет появится как /dev/my_spare_disk.

KERNEL=="hdb", DRIVER=="ide-disk", SYMLINK+="sparedisk"

Вышенаписанное правило гласит: при обнаружении устройства, которое имеет имя в ядре как hdb И для которого драйвер называется ide-disk, дать имя устройству по умолчанию и создать символический линк с именем sparedisk. Замечу, поскольку мы не указали имя устройства, udev использует имя по умолчанию. Чтобы сохранить стандартное содержимое /dev, наши собственные правила обычно  не включают в себя NAME, а создают несколько SYMLINK-ов и/или делают другие присвоения.

KERNEL=="hdc", SYMLINK+="cdrom cdrom0"

Вышеприведённое правило наверное более похоже на правила, которые вы может быть пишите. Оно создаёт два символьных линка /dev/cdrom и  /dev/cdrom0, каждый из которых указывает на /dev/hdc. Опять же , присвоения NAME не указано, так что,  будет использовано имя по умолчанию из ядра (hdc).

 

Соответствие с аттрибутами sysfs

Такое описание ключей соответствия, какое было дано, имеет весьма ограниченные возможности поиска по соответствию. В жизни нам нужен существенно лучший контроль: нам надо идентифицировать устройства по большему числу свойств, таких как коды поставщиков (vendor code), точный номер продукта(product number), серийные номера, объём ресурса хранения, номер раздела, и т.д.

Многие драйвера экспортируют подобную информацию в sysfs, а udev позволяет нам включить в наши правила поиск соответствия с данными sysfs. Для этого используется ключ ATTR, у которого немного другой синтаксис.

Вот пример правила, в котором ищется соответствие с одним из аттрибутом из sysfs. Далее в документе будут дополнительные подробности, которые помогут вам в написании правил, базирующихся на аттрибутах из sysfs.

SUBSYSTEM=="block", ATTR{size}=="234441648", SYMLINK+="my_disk"

 

Иерархия для устройств

Ядро Linux сегодня представляет нам устройства в виде деревообразных структур, и эта информация выдаётся через sysfs и может быть использована при написании правил. Например, мой жёсткий диск представлен как потомок от SCSI-диска ,  который в свою очередь является потомком Serial ATA контроллера, который в свою очередь потомок PCI-шины. Найти ту информацию, что вам нужна, вы вероятно сможете у родительского устройства, например, серийный номер моего жёсткого диска не представлен на уровне диска, а представлен в его прямом родителе на уровне SCSI-диска.

Четыре главных ключа соответствия, описанных далее (KERNEL/SUBSYSTEM/DRIVER/ATTR) соответствуют только значениям, которые указаны в запросе для устройства и не соответствуют значениям от родительских устройств. udev позволяет писать варианты ключей соответствия, которые будут просматривать дерево вверх.

  • KERNELS - поиск соответствия по имени устройства из ядра, или по имени из ядра любого устройства-родителя.
  • SUBSYSTEMS - поиск соответствия по подсиcтеме устройства, или по подсистеме любого устройства-родителя
  • DRIVERS - поиск по имени драйвера для устройства, или по имени драйвера любого устройства-родителя
  • ATTRS - поиск соответствия по аттрибуту устройства в sysfs,  или по аттрибуту sysfs любого устройства-родителя

Зная эти иерархические соглашения, вам может показаться, что написание правил слегка сложновато. В дальнейшем вы убедитесь, что существуют средства, которые облегчат вашу работу. О них будет рассказано далее.

 

Подстановка строк

При написании правил, которые обслуживают несколько схожих устройств, оказываются очень полезны операторы подстановки строк, схожие с printf. Вы можете просто вставить эти операторы в любое присвоение, которое делается вашим правилом, а udev во время выполнения выполнит подстановку.

Наиболее общими операторы - это %k и  %n. %k заменяется именем устройства из ядра, например , "sda3" для устройства, которое появится по умолчанию как /dev/sda3%n заменяется номером для устройства из ядра (для устройств хранения данных это номер раздела), например,  "3" для  /dev/sda3.

Для расширения функциональности udev такде предоставляет дополнительные подстановки. Проконсультируйтесь со страницами man для udev после прочтения оставшейся части этого документа. Для этих операторов, которые выше приведены в примерах, есть альтернативный синтаксис - $kernel и $number. В случаях, когда в правиле вам необходимо указать соответствие с  символом %, следует писать %%,  а для символа $ следует писать $$.

Для иллюстрации концепции подстановки строк, посмотрите на следующие правила:

KERNEL=="mice", NAME="input/%k"
KERNEL=="loop0", NAME="loop/%n", SYMLINK+="%k"

Первое правило заставляет создать узел устройства для мыши только в каталоге /dev/input (по умолчанию он должен был бы быть /dev/mice). Второе правило создаёт узел для устройства с именем loop0 как /dev/loop/0, и так же создаёт символический линк /dev/loop0, что является обычным именем для этого устройства.

Использование вышеприведённых правил весьма спорно, поскольку их можно переписать и без использования операторов подстановки. Настоящая мощь этих подстановок станет очевидной в следующей части.

 

Поиск совпадений строк

udev позволяет искать как точные совпадения строк, так и сравнение по образцу, как в шелле. Поддерживаются 3 образца:

  • * - отождествляется с любым символом, от 0 и более раз
  • ? - отождествляется с одним символом, ровно с одним
  • [] - отождествляется с любым одиночным символом, указанным в квадратных скобках, позволяется указывать диапазон символов

Вот несколько примеров, которые включают в себя указанные образцы. Обратите внимание на операторы подстановки строк.

KERNEL=="fd[0-9]*", NAME="floppy/%n", SYMLINK+="%k"
KERNEL=="hiddev*", NAME="usb/%k"

Первое правило соответствует всем флоппи-дисководам, и обеспечивает размещение узлов устройств в каталоге /dev/floppy, также как и создание символического линка с именем, которое должно было бы быть по умолчанию. Второе правило обеспечивает то, что устройства hiddev будут присутствовать только в каталоге /dev/usb.

Нахождение информации в sysfs

 

Дерево sysfs

Мы раньше уже немного затронули идею получения информации из sysfs. Чтобы писать правила, базирующиеся на этой информации, сначала следует знать имена  аттрибутов и их текущие значения.

sysfs на сегодня имеет очень простую структуру. Логически она поделена на каталоги. Каждый каталог содержит некоторое количество файлов (аттрибутов), которые обычно содержат всего одно значение. Также присутствуют некоторые символические линки, которые связывают устройства с их родителями. Мы ранее обсуждали эту иерархическую структуру.

Некоторые каталоги ссылаются на пути к устройствам верхнего уровня по дереву. Эти каталоги представляют текущие устройства, которые имеют соответствующие узлы устройств в /dev. Пути к устройствам верхнего уровня могут быть классифицированы как каталоги sysf, которые содержат файл dev.  Вы можете посмотреть из список с помощью следующей команды:

# find /sys -name dev

Например, в моей системе каталог /sys/block/sda - это путь к устройству, соответствующему моему жёсткому диску. Он слинкован со своим родителем, SCSI диском, через символическую связь the /sys/block/sda/device.

Когда вы пишете правило, основываясь на информации из sysfs, вы просто ищете соответствие значения аттрибута в каких-то файлах из части цепочки. Например, я могу прочесть размер моего жёсткого диска так:

# cat /sys/block/sda/size
234441648

В правиле для udev я могу использовать ATTR{size}=="234441648" для того, чтобы идентифицировать этот диск. udev проходит перебором по всей цепочке для устройства, и как альтернативу я могу искать совпадение в другой части цепи(например аттрибуты в /sys/class/block/sda/device/) используя ATTRS. Однако есть некоторые предостережения при работе с другими частями цепочки. Об этом будет рассказано позже.

Хотя вышесказанное и может пригодиться как знакомство со структурой sysfs и как точное представление о том, как udev ищет соответствие значений, выуживание вручную данных из sysfs отнимет много времени и вообщем-то необязательно.

 

udevinfo

Запустить программу udevinfo, может оказаться самым прямым путём, который можно использовать для создания правил. Всё, что вам надо знать - это путь к устройству в sysfs, чтобы правильно сформулировать запрос. Пример запроса показан ниже:

# udevinfo -a -p /sys/block/sda

  looking at device '/block/sda':
    KERNEL=="sda"
    SUBSYSTEM=="block"
    ATTR{stat}=="  128535     2246  2788977   766188    73998   317300  3132216  5735004        0   516516  6503316"
    ATTR{size}=="234441648"
    ATTR{removable}=="0"
    ATTR{range}=="16"
    ATTR{dev}=="8:0"

  looking at parent device '/devices/pci0000:00/0000:00:07.0/host0/target0:0:0/0:0:0:0':
    KERNELS=="0:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{ioerr_cnt}=="0x0"
    ATTRS{iodone_cnt}=="0x31737"
    ATTRS{iorequest_cnt}=="0x31737"
    ATTRS{iocounterbits}=="32"
    ATTRS{timeout}=="30"
    ATTRS{state}=="running"
    ATTRS{rev}=="3.42"
    ATTRS{model}=="ST3120827AS     "
    ATTRS{vendor}=="ATA     "
    ATTRS{scsi_level}=="6"
    ATTRS{type}=="0"
    ATTRS{queue_type}=="none"
    ATTRS{queue_depth}=="1"
    ATTRS{device_blocked}=="0"

  looking at parent device '/devices/pci0000:00/0000:00:07.0':
    KERNELS=="0000:00:07.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="sata_nv"
    ATTRS{vendor}=="0x10de"
    ATTRS{device}=="0x037f"

Как вы видите, udevinfo просто выдаёт список аттрибутов, которые вы можете использовать "как есть" в ключах соответствия ваших правил для udev. Из вышеприведённого примера я могу создать (для примера) следующие два правила для этого устройства:

SUBSYSTEM=="block", ATTR{size}=="234441648", NAME="my_hard_disk"
SUBSYSTEM=="block", SUBSYSTEMS=="scsi", ATTRS{model}=="ST3120827AS", NAME="my_hard_disk"

Вы может заметили выделение цветом в этих примерах. Здесь продемонстрировано, что допустимо комбинировать аттрибуты запрашиваемого устройства и одного родительского устройства. Но вы не должны смешивать в поиске аттрибуты от разных родительских устройств - такое правило работать не будет. Например, следующее правило недопустимо, поскольку пытается найти соответствие от двух родительских устройств:

SUBSYSTEM=="block", ATTRS{model}=="ST3120827AS", DRIVERS=="sata_nv", NAME="my_hard_disk"

Обычно у вас есть множество аттрибутов и вы должны выбрать какие-то для создания своего правила. В большинстве случаев хочется выбрать аттрибуты, с помощью которых можно устойчиво идентифицировать устройство и так, чтобы это было понятно человеку. В вышеприведённых примерах я выбрал в качестве аттрибутов размер и модель моего диска. Я не использовал бессмысленные цифры типа ATTRS{iodone_cnt}=="0x31737".

Посмотрим на эффекты от иерархии в выводе udevinfo. Зелёная секция , соответствующая устройству в запросе, использует стандартные ключи соответствия, такие как KERNEL и  ATTR. Голубая и бордовая секции, соответствующие родительским устройствам используют варианты с поиском в родительских секциях, такие как SUBSYSTEMS и  ATTRS. Вот почему на самом деле довольно просто иметь дело со сложной иерархической структурой, надо всего лишь использовать точные значения, которые можно получить с помощью udevinfo.

Другое общее замечание касается текстовых аттрибутов в выводе udevinfo. К ним добавляются пробелы (для примера посмотрите на ST3120827AS). В ваших правилах вы можете указать дополнительные пробелы или выкинуть их совсем, как я и сделал.

Единственная сложность в использовании udevinfo это то, что вы должны знать вершину пути к устройству ( в вышеприведённом примере это /sys/block/sda). И эта вершина не всегда очевидна. Однако, поскольку в большинстве случаев вы пишите правила для узлов устройств, которые уже существуют, вы можете использовать udevinfo, чтобы определить путь к вершине дерева для устройства:

# udevinfo -a -p $(udevinfo -q path -n /dev/sda)

 

Альтернативные методы

Хотя udevinfo это наиболее прямой путь получения списка точных аттрибутов, из которых можно построить правила,  некоторые пользователи довольны и другими утилитами. Такие утилиты как usbview отображают схожий набор информации, большая часть которой может быть использована в правилах.

Продвинутые темы

 

Управление доступом и владельцами устройств

udev позволяет вам использовать дополнительные присвоения в правилах для управления аттрибутами доступа к устройству и прав владения на устройство.

Присвоение GROUP позволяет задать группу, которой будет принадлежать узел устройства. Ниже приведено правило, которое определяет, что все устройства фреймбуферы будут принадлежать группе video:

KERNEL=="fb[0-9]*", NAME="fb/%n", SYMLINK+="%k", GROUP="video"

Ключевое слово OWNER, быть может и менее полезно, но позволяет вам задать имя Unix-пользователя, который будет владельцем узла устройства. Предположим, что мы столкнулись с несколько необычной ситуацией, когда вам надо, чтобы пользователь john был владельцем накопителей на гибких дисках. Для этого вы можете использовать следующее правило:

KERNEL=="fd[0-9]*", OWNER="john"

udev по умолчанию создаёт узлы с Unix-правами 0660 (разрешено читать/писать владельцу и группе). Если требуется, вы можете заменить эти умолчания для конкретных устройств, используя правила с оператором присваивания MODE . Для примера, следующее правило определяет, что узел inotify должен быть доступен всем на чтение и запись:

KERNEL=="inotify", NAME="misc/%k", SYMLINK+="%k", MODE="0666"

 

Использование внешних программ для присвоения имён устройствам.

В некоторых случаях вам может потребоваться большая гибкость, чем позволяют вам стандартные правила udev. В таком случае вы можете попросить udev запустить внешнюю программу и использовать стандартный вывод из этой для присвоения имени устройству.

Для того, чтобы использовать эту возможность, вы просто указываете абсолютный путь к запускаемой программе (и любые другие параметры) в операторе присвоения PROGRAM, и затем используете какой-то из вариантов подстановки %c  в операторах присвоения NAME/SYMLINK.

Следующие примеры используют фиктивную программу /bin/device_namer.  device_namer использует один аргумент -  имя устройства из ядра. device_namer обрабатывает это имя устройства и выдаёт результат в stdout. Результат состоит из нескольких частей. Каждая часть - это отдельное слово. Части отделены друг от друга одним пробелом.

В первом примере предполагается, что device_namer выдаёт несколько частей, каждая из которых создаёт симлинк (альтернативное имя) для устройства из запроса к программе.

KERNEL=="hda", PROGRAM="/bin/device_namer %k", SYMLINK+="%c"

Следующий пример предполагает, что device_namer выдаёт два слова, первое - имя устройства, второе имя - для симлинка. Здесь мы показываем использование подстановки %c{N}, которая указывает на часть с номером N в выводе программы:

KERNEL=="hda", PROGRAM="/bin/device_namer %k", NAME="%c{1}", SYMLINK+="%c{2}"

Следующий пример предполагает, что device_namer выдаёт одно слово для имени устройства, за которым следуют слова, которые создают дополнительные симлинки. Здесь представлено то, как использовать подстановку %c{N+}, которая выдаёт раскрывается как части N, N+1, N+2, ... и так до конечного слова из вывода программы.

KERNEL=="hda", PROGRAM="/bin/device_namer %k", NAME="%c{1}", SYMLINK+="%c{2+}"

Выходные слова можно использовать с любым присваивающим ключевым словом, а не только с NAME и SYMLINK. Приводимый далее пример использует фиктивную программу, с помощью которой определяется Unix-группа, которой должно принадлежать устройство:

KERNEL=="hda", PROGRAM="/bin/who_owns_device %k", GROUP="%c"

 

Запуск внешних программ при определённых событиях.

Дополнительной причиной написания правил udev может послужить необходимость в запуске специальной программы в моменты, когда устройство подключают к системе или отключают. Например, вам хочется выполнить скрипт автоматического скачивания ваших фотографий с камеры после того, как камеру вы подключили к компьютеру.

Вас не должны вводить в заблуждение ранее описанные возможности PROGRAMPROGRAM используется для запуска программ которые выдают имена устройств (и эти программы ничего другого делать и не должны). В момент, когда эти программы выполняются, узел устройства ещё не создан, так что работать с устройством в этот момент никак не получится.

Далее описанная функциональность позволяет запустить программу уже после того, как узел устройства уже создан. В этом случае программа может работать с устройством, однако она не должна делать это долго, поскольку udev приостанавливается на время пока такая программа работает. Обойти это ограничение можно тем, что программа должна после запуска сразу отсоединиться.

Вот пример правила, которое демонстрирует оператор присвоения RUN, добавляющий указанную программу в список запускаемых:

KERNEL=="sdb", RUN+="/usr/bin/my_program"

Когда выполняется /usr/bin/my_program, некоторые переменные udev доступны через переменные окружения, включая такие значения, как SUBSYSTEM. Вы можете также использовать переменную окружения ACTION, чтобы определить было ли устройство подключено или отключено - значение ACTION будет или "add" или "remove" соответственно.

udev не запускает такие программы на каком-нибудь активном терминале, и не выполняет их через shell. Удостоверьтесь, что файлы программ имеют аттрибут исполняемости, а если это shell-скрипт, то удостоверьтесь, что первой строкой в скрипте указан правильный shell (т.е. #!/bin/sh), и не надейтесь увидеть на своём терминале то, что выводит программа в stdout.

 

Взаимодействие с окружением

udev позволяет использовать ключевое слово ENV key для получения значений из окружения как для поиска соответствия, так и для присваивания.

В случае присваивания, вы можете устанавливать переменные окружения, по содержимому которых вы позже можете проверить соответствие. Вы также можете устанавливать переменнные окружения и использовать из в любой внешней программе, которую запускаете при помощи ранее описанной техники. Ниже показан выдуманный пример правила, которое устанавливает переменную окружения.

KERNEL=="fd0", SYMLINK+="floppy", ENV{some_var}="value"

В случае поиска соответствия , вы можете заставить срабатыват правило в зависимости от значения внешней переменной. Замечу, что окружение udev вовсе те то,  что имеет пользователь за своей консолью. Ниже показано выдуманное правило, которое осуществляет поиск соответствия с переменной окружения.

KERNEL=="fd0", ENV{an_env_var}=="yes", SYMLINK+="floppy"

Это правило создаст ссылку /dev/floppy только если $an_env_var будет иметь значение "yes" в окружении udev's.

 

Дополнительные опции

Ещё одно присваивание может оказаться полезным. Это список OPTIONS. Доступно несколько опций:

  • all_partitions - создать все возможные разделы для блочного устройства, а не только те, что были найдены изначально
  • ignore_device - игнорировать события полностью
  • last_rule - гарантировать, что никакие более поздние правила применяться не будут

Например, следующее правило устанавливает принадлежность к группе для моего жёсткого диска, и гарантирует, что последующие правила применяться не будут:

KERNEL=="sda", GROUP="disk", OPTIONS+="last_rule"

Примеры

 

USB Принтер

Я включил свой принтер и узел устройства получил имя /dev/lp0. Это имя меня не устраивает, и я решил использовать udevinfo, чтобы написать правило, создающее другое имя:

# udevinfo -a -p $(udevinfo -q path -n /dev/lp0)
  looking at device '/class/usb/lp0':
    KERNEL=="lp0"
    SUBSYSTEM=="usb"
    DRIVER==""
    ATTR{dev}=="180:0"

  looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb1/1-1':
    SUBSYSTEMS=="usb"
    ATTRS{manufacturer}=="EPSON"
    ATTRS{product}=="USB Printer"
    ATTRS{serial}=="L72010011070626380"

Моё правило выглядит так:

SUBSYSTEM=="usb", ATTRS{serial}=="L72010011070626380", SYMLINK+="epson_680"

 

USB камера

Как и большинство камер, моя камера определяется как внешний жёсткий диск, подключенный через шину USB, и использует эмуляцию SCSI. Чтобы получить доступ к моим фотографиям, я монтирую устройство и копирую файлы на мой жёсткий диск.

Не все камеры так работают: некоторые из них используют протокол, который не представляет их как устройства хранения данных. Подобные камеры поддерживаются программой gphoto2. В случаях, когда с камерой работаешь через gphoto, вам не надо писать правила для устройства, поскольку оно полностью управляется из пространства пользователя (а не специальным драйвером ядра).

Сложностью работы с устройствами USB камеры является то, что она обычно показывает себя как диск с одним разделом, например как /dev/sdb с разделом /dev/sdb1. Узел sdb для меня бесполезен, но вот sdb1 нужен - это раздел, который хочу смонтировать. Поскольку в sysfs данные представлены в виде цепочки, нужные мне аттрибуты, которые udevinfo выдаёт для /dev/sdb1 полностью совпадают в выводом для /dev/sdb. Такие результаты в вашме правиле потенциально подходят как для всего диска, так и для раздела. И это не то, что вам хочется, так что ваше правило должно быть специфично.

Чтобы обойти проблему, надо подумать, в чём разница между sdb и sdb1. Ответ удивительно прост: имена отличаются, так что мы можем использовать простой образец, который бы соответствовал полю имени устройства:

# udevinfo -a -p $(udevinfo -q path -n /dev/sdb1)
  looking at device '/block/sdb/sdb1':
    KERNEL=="sdb1"
    SUBSYSTEM=="block"

  looking at parent device '/devices/pci0000:00/0000:00:02.1/usb1/1-1/1-1:1.0/host6/target6:0:0/6:0:0:0':
    KERNELS=="6:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{rev}=="1.00"
    ATTRS{model}=="X250,D560Z,C350Z"
    ATTRS{vendor}=="OLYMPUS "
    ATTRS{scsi_level}=="3"
    ATTRS{type}=="0"

Моё правило:

KERNEL=="sd?1", SUBSYSTEMS=="scsi", ATTRS{model}=="X250,D560Z,C350Z", SYMLINK+="camera"

 

Жёсткий USB диск

Жёсткий USB диск вполне похож на USB камеру, описанную выше. Однако образец того, как работать с ним, отличается. В примере с камерой меня не интересовал узел sdb - он нужен только при работе с разделами (т.е. при работе программы  fdisk). Ну зачем мне надо делать разделы в накопителе моей камеры!?

И конечно, когда у вас 100GB жёсткий USB диск,  вполне понятно, что вы можете захотеть разбить его на разделы. И для этого можно воспользоваться возможностями подстановки строк, которые есть в udev:

KERNEL=="sd*", SUBSYSTEMS=="scsi", ATTRS{model}=="USB 2.0 Storage Device", SYMLINK+="usbhd%n"

Это правило создаст следующие симлинки:

  • /dev/usbhd - Устройство, с которым работаем с помощью fdisk-а
  • /dev/usbhd1 - Первый раздел (монтируемый)
  • /dev/usbhd2 - Второй раздел (монтируемый)

 

USB кардридер

USB кардридеры (CompactFlash, SmartMedia, etc) это совсем другой диапазон USB устройств хранения, и при их использовании есть другие требования.

Эти устройства обычно не информируют компьютер о смене носителя. Так, если вы вставили устройство без носителя данныХ, а затем вставили в него карту памяти, компьютер об этом не узнает, и у вас не будет узла sdb1 для раздела, который вы могли бы смонтировать.

Одно из возможных решений - использовать возможности опции all_partitions, которая создасть 16 узлов для разделов для каждого блочного устройства, которое подходит под правило:

KERNEL="sd*", SUBSYSTEMS=="scsi", ATTRS{model}=="USB 2.0 CompactFlash Reader", SYMLINK+="cfrdr%n", OPTIONS+="all_partitions"

У вас теперь появятся узлы устройств с именами: cfrdr, cfrdr1, cfrdr2, cfrdr3, ..., cfrdr15.

 

USB Palm Pilot

Эти устройства работают как USB-serial,  так что по умолчанию у вас будет только узел устройства ttyUSB1.  Утилиты palm работают с /dev/pilot, так что, многие юзеры хотели бы использовать правило, которое обеспечит создание /dev/pilot.

Carsten Clasohm's blog post содержит то, как это сделать. Правило от Carsten-а представлено ниже:

SUBSYSTEMS=="usb", ATTRS{product}=="Palm Handheld", KERNEL=="ttyUSB*", SYMLINK+="pilot"

Отмечу, что строка для продукта может меняться от продукта к продукту, так что проверьте (используйте udevinfo) какая подойдёт в вашем случае.

 

CD/DVD приводы

У меня 2 оптических привода: DVD (hdc), и DVD-RW (hdd). Я не жду что, узды устройств поменяются, разве что я физически поменяю что-то в моей системе. Однако многим пользователям удобны такие названия как /dev/dvd.

Поскольку я знаю, как ядро именует эти устройства, правила очень просты. Вот пример для моей системы:

SUBSYSTEM=="block", KERNEL=="hdc", SYMLINK+="dvd", GROUP="cdrom"
SUBSYSTEM=="block", KERNEL=="hdd", SYMLINK+="dvdrw", GROUP="cdrom"

 

Сетевый интерфейсы

Хотя к ним мы и обращаемся по имени, сетевые интерфейсы обычно не имеют узлов устройств, связанных с ними. Несмотря на это, процесс создания правил для них практически такой же.

Имеет смысл в правиле просто найти соответствие с MAC-адресом вашего интерфейса, поскольку MAC-адреса уникальны. Однако удостоверьтесь, что вы используете абсолютно тот MAC-адрес, который выдаёт udevinfo, поскольку если регистр букв не совпадает полностью, правило работать не будет.

# udevinfo -a -p /sys/class/net/eth0
  looking at class device '/sys/class/net/eth0':
    KERNEL=="eth0"
    ATTR{address}=="00:52:8b:d5:04:48"

Вот моё правило:

KERNEL=="eth*", ATTR{address}=="00:52:8b:d5:04:48", NAME="lan"

Следует перегрузить драйвер для сетевой карты, чтобы правило сработало. Вы можете выгрузить и затем загрузить модуль или просто перезагрузить систему. Также вам потребуется изменить конфигурацию системы, чтобы она использовала "lan" а не "eth0".  У меня были некоторые проблемы с эти (интерфейс не переименовыволя) пока я полностью не удалил всё, что ссылалось на eth0. После этого вы сможете использовать "lan" вместо "eth0" в ifconfig или подобных утилитах.

Тестирование и отладка

 

Ввод в действие ваших правил

Если у вас ядро из последних с поддержкой inotify, udev автоматически контролирует содержимое каталога с вашими правилами и автоматически применит любые модификации, которые вы сделали в файлах правил.

Несмотря на сказанное, udev не будет автоматически заново обрабатывать все устройства и пытаться применить все новый правила. Например, вы написали правило, которое делает дополнительный симлинк для вашей камеры, а камера уже была подключена к системе. В этом случае вам не стоит ожидать, что симлинк сразу появится.

Чтобы он появился, вам надо отключить и заново подключить камеру. Альтернативой для непереподключаемых устройств служит запуск программы udevtrigger.

Если ядро не поддерживает inotify, новые правила автоматически не определятся. В таком случае следует запустить udevcontrol reload_rules после изменения файлов с правилами.

 

udevtest

Если вы знаете верхнюю точку пути к устройству в sysfs,  вы можете использовать udevtest  для просмотра действий, которые предпримет udev.  Это может помочь в отладке ваших правил. Предположим, что вы хотите отладить правило, действующее на/sys/class/sound/dsp:

# udevtest /class/sound/dsp
main: looking at device '/class/sound/dsp' from subsystem 'sound'
udev_rules_get_name: add symlink 'dsp'
udev_rules_get_name: rule applied, 'dsp' becomes 'sound/dsp'
udev_device_event: device '/class/sound/dsp' already known, remove possible symlinks
udev_node_add: creating device node '/dev/sound/dsp', major = '14', minor = '3', mode = '0660', uid = '0', gid = '18'
udev_node_add: creating symlink '/dev/dsp' to 'sound/dsp'

Отмечу, что префикс /sys был удалён из командной строки для udevtest. Это потому, что udevtest работает с путями к устройствам. Также замечу, что udevtest - это чисто тестовое/отладочное средство, оно не создаёт узлов устройств, несмотря на содержимое того, что эта программа выводит на экран!

 

Автор и контакты

This document is written by Daniel Drake <dan@reactivated.net>. Feedback is appreciated.

For support, you should mail the linux-hotplug mailing list: linux-hotplug-devel@lists.sourceforge.net.

Copyright (C) 2003-2006 Daniel Drake.
This document is licensed under the GNU General Public License, Version 2.