Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Тематический каталог -> Настройка системы

UDEV: Как установить свои правила

Оригинал: Writing udev rules
Автор: Daniel Drake (dsd)
Версия 0.74
Свободный перевод: Алексей Дмитриев
Дата перевода: 19 августа 2008

Содержание:

Введение

Об этой статье

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

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

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

Статья не охватывает каждый нюанс установки правил, но претендует на описание всех основных концепций. Тонкие детали требуют изучения манов.

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

Общее представление, концепция

Термины: devfs, sysfs, nodes, etc

Это - только базовое введение; местами оно может быть не совсем строго.

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

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

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

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

Зачем это нужно?

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

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

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

У постоянных имен нод устройств есть ряд преимуществ. Предположим, у вас есть два 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

Когда я подключаю свой USB флеш диск, udev создает ноду-
/dev/disk/by-id/usb-Prolific_Technology_Inc._USB_Mass_Storage_Device-part1,
что также является постоянным именем.

Установление правил

Файлы правил и их семантика (смысл слов и символов)

Решая вопрос наименования устройства и характер дополнительных действий, 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 не остановится, найдя соответствующее устройству правило, но будет продолжать поиск и пытаться выполнить каждое правило, о котором знает.

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

Каждое правило представляет собой цепочку пар ключ-значение, и пары эти разделены запятыми. Ключ соответствия (match key) является условием для идентификации устройства, к которому относится правило. Когда ВСЕ ключи соответствия в данном правиле относятся к обрабатываемому устройству, тогда правило начинает применяться, и активизируются ключи назначения (assignment key).

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

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

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

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

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

Для установления правил, udev обеспечивает несколько различных ключей соответствия (match key), которые должны точно соответствовать устройствам. Несколько обычных ключей представлены ниже, с остальными мы познакомимся позже в этой статье. Полный список доступен на страницах манов.
  • KERNEL - устанавливает соответствие с именем, данным ядром для устройства
  • SUBSYSTEM - устанавливает соответствие подсистемы устройства
  • DRIVER - устанавливает соответствие имени драйвера, поддерживающего устройство
После того, как вы использовали серию ключей соответствия для точного указания устройства, udev предоставляет вам хороший контроль над дальнейшими действиями при помощи ряда ключей назначения (assignment key). Полный список возможных ключей назначения вы найдете на странице ман udev. Основные ключи назначения представлены ниже. Другие будут описаны позже в этой статье.

NAME - имя, которое должно быть присвоено ноде устройства.

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

Как уже отмечалось ранее, udev создает только одну настоящую ноду устройства для одного устройства. Если вы хотите обеспечить этой ноде устройства альтернативные имена, то пользуйтесь возможностями символических ссылок. При помощи ключа назначения (assignment key) 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

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

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

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

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

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

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

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

KERNELS - устанавливает соответствие с именем, данным ядром для устройства; или именем, данным ядром любому из родительских устройств.

SUBSYSTEMS - устанавливает соответствие подсистемы устройства; или подсистему любого родительского устройства.

DRIVERS - устанавливает соответствие имени драйвера, поддерживающего устройство; или имя драйвера, поддерживающего любое родительское устройство.

ATTRS - устанавливает соответствие с атрибутом sysfs устройства; или атрибутом sysfs любого родительского устройства.

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

Подстановка, или замена строк

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

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

В распоряжении udev есть еще несколько операторов подстановки для повышенной функциональности. Проконсультируйтесь с 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 поддерживает подстановку по шаблону, как в оболочке (shell). Существуют 3 поддерживаемых шаблона:
  • * - заменяет любой символ, любое число раз, в том числе ни одного
  • ? - заменяет любой символ, строго один раз
  • [] - заменяет любой один символ, указанный в скобках, разрешается также диапазон или интервал

Вот несколько примеров, включающих перечисленные шаблоны. Обратите внимание на операторы подстановки строк.

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

Это правило устанавливает соответствие со всеми флоппи дисководами, обеспечивает размещение нод устройств в директории /dev/floppy, а также создает символическую ссылку на имя по умолчанию.

KERNEL=="hiddev*", NAME="usb/%k"

Второе правило обеспечивает присутствие устройств hiddev только в директории /dev/usb.

Получение информации из sysfs

Дерево sysfs

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

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

К некоторым директориям обращаются как к путям устройств (paths) высшего уровня. Такие директории представляют реальные устройства, имеющие соответствующие ноды устройств. Пути устройств высшего уровня, могут быть классифицированы как директории sysfs, содержащие dev файл; их список можно просмотреть при помощи следующей команды:

# find /sys -name dev

Для примера, на моей системе, директория /sys/block/sda является путем устройства моего жесткого диска. При помощи символической ссылки /sys/block/sda/device он слинкован со своим родителем, устройством SCSI диск.

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

# cat /sys/block/sda/size
234441648

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

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

udevinfo

Введите команду udevinfo, самый простой инструмент для составления правил udev. Все, что нужно знать, это 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 состоит в необходимости точно знать полные пути устройств (paths) (в приведенном примере это /sys/block/sda). А эти пути не всегда очевидны. Однако, учитывая тот факт, что вы обычно устанавливаете правила для нод устройств, которые уже существуют, вы можете "попросить" udevinfo найти пути устройства для вас:

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

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

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

Более сложные правила

Установка прав доступа и прав собственности

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

Ключ назначения GROUP позволяет установить, какая Юникс группа будет владеть нодой устройства. Вот пример правила, устанавливающего, что группа video владеет устройствами framebuffer'а:

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

Ключ назначения OWNER, возможно малоупотребительный, позволяет вам назначить владельца Юникс для ноды устройства. Не вдаваясь в довольно странную ситуацию, когда вы предаете право собственности на ваши флоппи устройства некоему john'у, вы можете составить такое правило:

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

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

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

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

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

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

Следующие примеры относятся к вымышленной программе device_namer, находящейся в директории /bin; таким образом, ее абсолютный путь /bin/device_namer. Программа device_namer берет один аргумент командной строки, точнее - имя, присвоенное ядром устройству. Исходя из имени, данного ядром, программа device_namer производит некие действия, результат которых, разбитый на несколько частей, выдает на обычный конвейер (stdout pipe). Каждая часть является просто одним словом, и части отделяются одним пробелом.

В нашем первом примере, мы подразумеваем, что 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. Следующий пример использует фиктивную программу для назначения Юникс группы, которая будет владеть устройством:

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

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

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

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

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

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

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

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

udev не запускает такие программы на любом активном терминале, и не запускает их в контексте оболочки (shell). Убедитесь, что ваша программа является выполняемой (executable); если это скрипт шелла, убедитесь, что он стартует с соответствующим shebang http://en.wikipedia.org/wiki/Shebang_(Unix) (например, #!/bin/sh), и не ожидайте появления на вашем терминале стандартного вывода.

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

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

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

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

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.

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

Весьма полезным может оказаться ключ присоединения OPTIONS. Список этих опций невелик:
  • all_partitions - создает все возможные разделы для блочного устройства, а не только те, что были первоначально зафиксированы
  • ignore_device - полностью игнорировать событие
  • 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:
    SUBSYSTEMS=="usb"
    ATTRS{manufacturer}=="EPSON"
    ATTRS{product}=="USB Printer"
    ATTRS{serial}=="L7201001107062638

И я составляю вот такое правило:

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

USB камера

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

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

Обычная сложность с USB камерой состоит в том, что она обычно идентифицирует себя как диск с единственным разделом, в нашем случае диск /dev/sdb с разделом /dev/sdb1. Нода sdb для меня бесполезна, но нода sdb1 интересна - именно ее я хочу примонтировать. Тут кроется проблема: так как sysfs представляет собой цепочку, то и полезные атрибуты, выработанные udevinfo для /dev/sdb1 идентичны таковым для /dev/sdb. Это приведет к тому, что потенциально ваше правило будет относиться как ко всему диску, так и к разделу, что вас не может устроить; правило должно быть однозначно.

Чтобы выйти из этого затруднения, нужно подумать: "А какая разница между sdb и sdb1?" Ответ ошеломительно прост - разница в самом названии. Так что мы можем использовать обычный шаблон (pattern) приводящий в соответствие в поле NAME.

# 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 винчестер похож на USB камеру, описанную выше, однако типичное использование шаблонов отличается. В случае с камерой, я объяснял, что меня не интересовала нода sdb, единственное назначение которой - создание разделов (скажем с помощью fdisk), но зачем я стану создавать разделы в камере!?

Конечно, если у вас 100Гб USB винчестер, вполне понятно желание разбить его на разделы. В этом случае, мы используем преимущества подстановки строк:

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

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

/dev/usbhd - Нода, к которой можно применять fdisk

/dev/usbhd1 - Первый раздел (монтируемый)

/dev/usbhd2 - Второй раздел (монтируемый)

USB Card Reader (читатель флеш карт)

USB Card Reader (читатель флеш карт) USB Card Reader (CompactFlash, SmartMedia, и другие) - это другой тип 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, так что многие захотят составить правило для обеспечения этого.

Carsten Clasohm's blog post http://www.clasohm.com/blog/one-entry?entry%5fid=12096 является прекрасным источником для этого дела. Правило Carsten'а приведено ниже:

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

Заметьте, что атрибут с названием продукта будет другим для других моделей, так что проверьте (при помощи udevinfo), какой именно соответствует вашему.

CD/DVD дисководы

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

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

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

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

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

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

Имеет смысл просто указать 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

udevtest Если вы знаете в sysfs полные пути (path), то можете пользоваться программой 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 prefix был удален из числа аргументов командной строки udevtest, потому что udevtest оперирует с путями (path) устройства. Также заметьте, что udevtest является чисто тестовым и отладочным инструментом, он не создает ноды устройств, невзирая на предложения вывода этой программы!

Об авторе и контактах

Этот документ написан Daniel Drake (dan@reactivated.net). Обратная связь приветствуется.

Для поддержки подпишитесь на 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.