Как писать правила Udev: различия между версиями
Нет описания правки |
|||
(не показано 25 промежуточных версий этого же участника) | |||
Строка 129: | Строка 129: | ||
== Базовые правила == | == Базовые правила == | ||
udev даёт несколько ключей соответствия, которые можно использовать при написании правил, и по которым очень точно устанавливается соответствие с устройством. Некоторые из наиболее общих ключей представлены ниже, остальные будут представлены в этом документе позднее. Чтобы увидеть полный список, читайте страницы манов по udev. | udev даёт несколько ключей соответствия, которые можно использовать при написании правил, и по которым очень точно устанавливается соответствие с устройством. Некоторые из наиболее общих ключей представлены ниже, остальные будут представлены в этом документе позднее. Чтобы увидеть полный список, читайте страницы манов по udev. | ||
*'''KERNEL''' - соответствие с именем устройства в ядре | |||
*'''SUBSYSTEM''' - соответствие с подсистемой устройства | |||
*'''DRIVER''' - соответствие с именем драйвера, обслуживающего устройство | |||
После того, как вы использовали список ключей соответствия, чтобы точно определить устройство, udev позволяет вам удобно управлять тем, что делать далее с помощью набора присваивающих ключей. Чтобы увидеть полный список возможных присваивающих ключей, читайте маны udev. Наиболее общие присваивающие ключи представлены ниже. Остальные будут представлены в этом документе позднее. | После того, как вы использовали список ключей соответствия, чтобы точно определить устройство, udev позволяет вам удобно управлять тем, что делать далее с помощью набора присваивающих ключей. Чтобы увидеть полный список возможных присваивающих ключей, читайте маны udev. Наиболее общие присваивающие ключи представлены ниже. Остальные будут представлены в этом документе позднее. | ||
*'''NAME''' - имя, которое будет использовано для узла устройства | |||
*'''SYMLINK''' - '''список''' символических линков, которые действуют как альтернативные имена для узла устройства | |||
Как можно догадаться из вышесказанного, udev создаёт один реальный узел для одного устройства. Есил вы хотите иметь ещё альтернативные имена для этого узла, используйте возможность создания символических линков. С помощью ''SYMLINK'', вы действительно поддерживаете список символических линков, каждый из которых указывает на реальный узел устройства. Для манипуляций этими линками используйте другой оператор, которым можно добавлять что-то в списки: '''+='''. Вы можете добавлять в любом правиле несколько символических линков в список, разделяя каждое добавление пробелом. | |||
KERNEL=="hdb", NAME="my_spare_disk" | |||
Вышенаписанное правило гласит: при обнаружении устройства, которое имеет | Вышенаписанное правило гласит: ''при обнаружении устройства, которое имеет в ядре имя hdb, вместо того, чтобы назвать его hdb, дать имя узлу устройства my_spare_disk''. В результате узел устройства будет появится как {{path|/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). | Вышеприведённое правило наверное более похоже на правила, которые вы может быть пишите. Оно создаёт два символьных линка ''/dev/cdrom'' и ''/dev/cdrom0'', каждый из которых указывает на ''/dev/hdc''. Опять же , присвоения NAME не указано, так что, будет использовано имя по умолчанию из ядра (hdc). | ||
==Соответствие с аттрибутами sysfs== | ==Соответствие с аттрибутами sysfs== | ||
Такое описание ключей соответствия, какое было дано, имеет весьма ограниченные возможности поиска по соответствию. В жизни нам нужен существенно лучший контроль: нам надо идентифицировать устройства по большему числу свойств, таких как коды поставщиков (vendor code), точный номер продукта(product number), серийные номера, объём ресурса хранения, номер раздела, и т.д. | Такое описание ключей соответствия, какое было дано, имеет весьма ограниченные возможности поиска по соответствию. В жизни нам нужен существенно лучший контроль: нам надо идентифицировать устройства по большему числу свойств, таких как коды поставщиков (vendor code), точный номер продукта (product number), серийные номера, объём ресурса хранения, номер раздела, и т.д. | ||
Многие драйвера экспортируют подобную информацию в sysfs, а udev позволяет нам включить в наши правила поиск соответствия с данными sysfs. Для этого используется ключ ATTR, у которого немного другой синтаксис. | Многие драйвера экспортируют подобную информацию в sysfs, а udev позволяет нам включить в наши правила поиск соответствия с данными sysfs. Для этого используется ключ ''ATTR'', у которого немного другой синтаксис. | ||
Вот пример правила, в котором ищется соответствие с одним из аттрибутом из sysfs. Далее в документе будут дополнительные подробности, которые помогут вам в написании правил, базирующихся на аттрибутах из sysfs. | Вот пример правила, в котором ищется соответствие с одним из аттрибутом из sysfs. Далее в документе будут дополнительные подробности, которые помогут вам в написании правил, базирующихся на аттрибутах из sysfs. | ||
SUBSYSTEM=="block", ATTR{size}=="234441648", SYMLINK+="my_disk" | |||
== Иерархия для устройств == | == Иерархия для устройств == | ||
Ядро Linux сегодня представляет нам устройства в виде деревообразных структур, и эта информация выдаётся через sysfs и может быть использована при написании правил. Например, мой жёсткий диск представлен как потомок от SCSI-диска , который в свою очередь является потомком Serial ATA контроллера, который в свою очередь потомок PCI-шины. Найти ту информацию, что вам нужна, вы вероятно сможете у родительского устройства, например, серийный номер моего жёсткого диска не представлен на уровне диска, а представлен в его прямом родителе на уровне SCSI-диска. | Ядро Linux сегодня представляет нам устройства в виде деревообразных структур, и эта информация выдаётся через sysfs и может быть использована при написании правил. Например, мой жёсткий диск представлен как потомок от SCSI-диска , который в свою очередь является потомком Serial ATA контроллера, который в свою очередь потомок PCI-шины. Найти ту информацию, что вам нужна, вы вероятно сможете у родительского устройства, например, серийный номер моего жёсткого диска не представлен на уровне диска, а представлен в его прямом родителе на уровне SCSI-диска. | ||
Четыре главных ключа соответствия, описанных далее (KERNEL/SUBSYSTEM/DRIVER/ATTR) соответствуют только значениям, которые указаны в запросе для устройства и не соответствуют значениям от родительских устройств. udev позволяет писать варианты ключей соответствия, которые будут просматривать дерево вверх. | Четыре главных ключа соответствия, описанных далее (KERNEL/SUBSYSTEM/DRIVER/ATTR) соответствуют только значениям, которые указаны в запросе для устройства и не соответствуют значениям от родительских устройств. udev позволяет писать варианты ключей соответствия, которые будут просматривать дерево вверх. | ||
*'''KERNELS''' - поиск соответствия по имени устройства из ядра, или по имени из ядра любого устройства-родителя. | |||
*'''SUBSYSTEMS''' - поиск соответствия по подсиcтеме устройства, или по подсистеме любого устройства-родителя | |||
*'''DRIVERS''' - поиск по имени драйвера для устройства, или по имени драйвера любого устройства-родителя | |||
*'''ATTRS''' - поиск соответствия по аттрибуту устройства в sysfs, или по аттрибуту sysfs любого устройства-родителя | |||
Зная эти иерархические соглашения, вам может показаться, что написание правил слегка сложновато. В дальнейшем вы убедитесь, что существуют средства, которые облегчат вашу работу. О них будет рассказано далее. | Зная эти иерархические соглашения, вам может показаться, что написание правил слегка сложновато. В дальнейшем вы убедитесь, что существуют средства, которые облегчат вашу работу. О них будет рассказано далее. | ||
==Подстановка строк== | ==Подстановка строк== | ||
При написании правил, которые обслуживают несколько схожих устройств, оказываются очень полезны операторы подстановки строк, схожие с printf. Вы можете просто вставить эти операторы в любое присвоение, которое делается вашим правилом, а udev во время выполнения выполнит подстановку. | При написании правил, которые обслуживают несколько схожих устройств, оказываются очень полезны операторы подстановки строк, схожие с printf. Вы можете просто вставить эти операторы в любое присвоение, которое делается вашим правилом, а udev во время выполнения выполнит подстановку. | ||
Наиболее общими операторы - это %k и %n. %k заменяется именем устройства из ядра, например , "sda3" для устройства, которое появится по умолчанию как /dev/sda3. %n заменяется номером для устройства из ядра (для устройств хранения данных это номер раздела), например, "3" для /dev/sda3. | Наиболее общими операторы - это '''%k''' и '''%n'''. %k заменяется именем устройства из ядра, например, "sda3" для устройства, которое появится по умолчанию как ''/dev/sda3''. '''%n''' заменяется номером для устройства из ядра (для устройств хранения данных это номер раздела), например, "3" для ''/dev/sda3''. | ||
Для расширения функциональности udev | Для расширения функциональности 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'', что является обычным именем для этого устройства. | |||
Первое правило заставляет создать узел устройства для мыши только в каталоге /dev/input (по умолчанию он должен был бы быть /dev/mice). Второе правило создаёт узел для устройства с именем loop0 как /dev/loop/0, и так же создаёт символический линк /dev/loop0, что является обычным именем для этого устройства. | |||
Использование вышеприведённых правил весьма спорно, поскольку их можно переписать и без использования операторов подстановки. Настоящая мощь этих подстановок станет очевидной в следующей части. | Использование вышеприведённых правил весьма спорно, поскольку их можно переписать и без использования операторов подстановки. Настоящая мощь этих подстановок станет очевидной в следующей части. | ||
==Поиск совпадений строк== | ==Поиск совпадений строк== | ||
udev позволяет искать как точные совпадения строк, так и сравнение по образцу, как в шелле. Поддерживаются 3 образца: | udev позволяет искать как точные совпадения строк, так и сравнение по образцу, как в шелле. Поддерживаются 3 образца: | ||
*'''<nowiki>*</nowiki>''' - отождествляется с любым символом, от 0 и более раз | |||
*'''<nowiki>?</nowiki>''' - отождествляется с одним символом, ровно с одним | |||
*'''<nowiki>[]</nowiki>''' - отождествляется с любым одиночным символом, указанным в квадратных скобках, позволяется указывать диапазон символов | |||
Вот несколько примеров, которые включают в себя указанные образцы. Обратите внимание на операторы подстановки строк. | Вот несколько примеров, которые включают в себя указанные образцы. Обратите внимание на операторы подстановки строк. | ||
KERNEL=="fd[0-9]*", NAME="floppy/%n", SYMLINK+="%k" | |||
KERNEL=="hiddev*", NAME="usb/%k" | |||
Первое правило соответствует всем флоппи-дисководам, и обеспечивает размещение узлов устройств в каталоге ''/dev/floppy'', также как и создание символического линка с именем, которое должно было бы быть по умолчанию. Второе правило обеспечивает то, что устройства hiddev будут присутствовать только в каталоге ''/dev/usb''. | |||
Первое правило соответствует всем флоппи-дисководам, и обеспечивает размещение узлов устройств в каталоге /dev/floppy, также как и создание символического линка с именем, которое должно было бы быть по умолчанию. Второе правило обеспечивает то, что устройства hiddev будут присутствовать только в каталоге /dev/usb. | |||
=Нахождение информации в sysfs= | =Нахождение информации в sysfs= | ||
Строка 223: | Строка 209: | ||
sysfs на сегодня имеет очень простую структуру. Логически она поделена на каталоги. Каждый каталог содержит некоторое количество файлов (аттрибутов), которые обычно содержат всего одно значение. Также присутствуют некоторые символические линки, которые связывают устройства с их родителями. Мы ранее обсуждали эту иерархическую структуру. | sysfs на сегодня имеет очень простую структуру. Логически она поделена на каталоги. Каждый каталог содержит некоторое количество файлов (аттрибутов), которые обычно содержат всего одно значение. Также присутствуют некоторые символические линки, которые связывают устройства с их родителями. Мы ранее обсуждали эту иерархическую структуру. | ||
Некоторые каталоги ссылаются на пути к устройствам верхнего уровня по дереву. Эти каталоги представляют текущие устройства, которые имеют соответствующие узлы устройств в /dev. Пути к устройствам верхнего уровня могут быть классифицированы как каталоги sysf, которые содержат файл dev. Вы можете посмотреть из список с помощью следующей команды: | Некоторые каталоги ссылаются на пути к устройствам верхнего уровня по дереву. Эти каталоги представляют текущие устройства, которые имеют соответствующие узлы устройств в /dev. Пути к устройствам верхнего уровня могут быть классифицированы как каталоги sysf, которые содержат файл ''dev''. Вы можете посмотреть из список с помощью следующей команды: | ||
# find /sys -name dev | |||
Например, в моей системе каталог /sys/block/sda - это путь к устройству, соответствующему моему жёсткому диску. Он слинкован со своим родителем, SCSI диском, через символическую связь the /sys/block/sda/device. | Например, в моей системе каталог ''/sys/block/sda'' - это путь к устройству, соответствующему моему жёсткому диску. Он слинкован со своим родителем, SCSI диском, через символическую связь the ''/sys/block/sda/device''. | ||
Когда вы пишете правило, основываясь на информации из sysfs, вы просто ищете соответствие значения аттрибута в каких-то файлах из части цепочки. Например, я могу прочесть размер моего жёсткого диска так: | Когда вы пишете правило, основываясь на информации из sysfs, вы просто ищете соответствие значения аттрибута в каких-то файлах из части цепочки. Например, я могу прочесть размер моего жёсткого диска так: | ||
# cat /sys/block/sda/size | |||
234441648 | |||
В правиле для udev я могу использовать ATTR{size}=="234441648" для того, чтобы идентифицировать этот диск. udev проходит перебором по всей цепочке для устройства, и как альтернативу я могу искать совпадение в другой части цепи(например аттрибуты в /sys/class/block/sda/device/) используя ATTRS. Однако есть некоторые предостережения при работе с другими частями цепочки. Об этом будет рассказано позже. | В правиле для udev я могу использовать {{path|ATTR{size}=="234441648"}} для того, чтобы идентифицировать этот диск. udev проходит перебором по всей цепочке для устройства, и как альтернативу я могу искать совпадение в другой части цепи(например аттрибуты в /sys/class/block/sda/device/) используя ATTRS. Однако есть некоторые предостережения при работе с другими частями цепочки. Об этом будет рассказано позже. | ||
Хотя вышесказанное и может пригодиться как знакомство со структурой sysfs и как точное представление о том, как udev ищет соответствие значений, выуживание вручную данных из sysfs отнимет много времени и вообщем-то необязательно. | Хотя вышесказанное и может пригодиться как знакомство со структурой sysfs и как точное представление о том, как udev ищет соответствие значений, выуживание вручную данных из sysfs отнимет много времени и вообщем-то необязательно. | ||
==udevinfo== | ==udevinfo== | ||
Запуск программы ''udevinfo'', может оказаться самым прямым путём, который можно использовать для создания правил. Всё, что вам надо знать - это путь к устройству в sysfs, чтобы правильно сформулировать запрос. Пример запроса показан ниже: | |||
# udevinfo -a -p /sys/block/sda | |||
looking at device '/block/sda': | looking at device '/block/sda': | ||
KERNEL=="sda" | KERNEL=="sda" | ||
Строка 253: | Строка 237: | ||
ATTR{range}=="16" | ATTR{range}=="16" | ||
ATTR{dev}=="8:0" | ATTR{dev}=="8:0" | ||
looking at parent device '/devices/pci0000:00/0000:00:07.0/host0/target0:0:0/0:0:0: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" | KERNELS=="0:0:0:0" | ||
Строка 272: | Строка 256: | ||
ATTRS{queue_depth}=="1" | ATTRS{queue_depth}=="1" | ||
ATTRS{device_blocked}=="0" | ATTRS{device_blocked}=="0" | ||
looking at parent device '/devices/pci0000:00/0000:00:07.0': | looking at parent device '/devices/pci0000:00/0000:00:07.0': | ||
KERNELS=="0000:00:07.0" | KERNELS=="0000:00:07.0" | ||
Строка 282: | Строка 266: | ||
Как вы видите, udevinfo просто выдаёт список аттрибутов, которые вы можете использовать "как есть" в ключах соответствия ваших правил для udev. Из вышеприведённого примера я могу создать (для примера) следующие два правила для этого устройства: | Как вы видите, 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". | Обычно у вас есть множество аттрибутов и вы должны выбрать какие-то для создания своего правила. В большинстве случаев хочется выбрать аттрибуты, с помощью которых можно устойчиво идентифицировать устройство и так, чтобы это было понятно человеку. В вышеприведённых примерах я выбрал в качестве аттрибутов размер и модель моего диска. Я не использовал бессмысленные цифры типа ATTRS{iodone_cnt}=="0x31737". | ||
Строка 296: | Строка 279: | ||
Единственная сложность в использовании udevinfo это то, что вы должны знать вершину пути к устройству ( в вышеприведённом примере это /sys/block/sda). И эта вершина не всегда очевидна. Однако, поскольку в большинстве случаев вы пишите правила для узлов устройств, которые уже существуют, вы можете использовать udevinfo, чтобы определить путь к вершине дерева для устройства: | Единственная сложность в использовании udevinfo это то, что вы должны знать вершину пути к устройству ( в вышеприведённом примере это /sys/block/sda). И эта вершина не всегда очевидна. Однако, поскольку в большинстве случаев вы пишите правила для узлов устройств, которые уже существуют, вы можете использовать udevinfo, чтобы определить путь к вершине дерева для устройства: | ||
# udevinfo -a -p $(udevinfo -q path -n /dev/sda) | |||
==Альтернативные методы== | ==Альтернативные методы== | ||
Хотя udevinfo это наиболее прямой путь получения списка точных атрибутов, из которых можно построить правила, некоторые пользователи довольны и другими утилитами. Такие утилиты как [http://www.kroah.com/linux/usb/ usbview] отображают схожий набор информации, большая часть которой может быть использована в правилах. | |||
Хотя udevinfo это наиболее прямой путь получения списка точных | |||
=Продвинутые темы= | =Продвинутые темы= | ||
Строка 311: | Строка 291: | ||
udev позволяет вам использовать дополнительные присвоения в правилах для управления аттрибутами доступа к устройству и прав владения на устройство. | udev позволяет вам использовать дополнительные присвоения в правилах для управления аттрибутами доступа к устройству и прав владения на устройство. | ||
Присвоение GROUP позволяет задать группу, которой будет принадлежать узел устройства. Ниже приведено правило, которое определяет, что все устройства фреймбуферы будут принадлежать группе video: | Присвоение ''GROUP'' позволяет задать группу, которой будет принадлежать узел устройства. Ниже приведено правило, которое определяет, что все устройства фреймбуферы будут принадлежать группе ''video'': | ||
KERNEL=="fb[0-9]*", NAME="fb/%n", SYMLINK+="%k", GROUP="video" | |||
Ключевое слово OWNER, быть может и менее полезно, но позволяет вам задать имя Unix-пользователя, который будет владельцем узла устройства. Предположим, что мы столкнулись с несколько необычной ситуацией, когда вам надо, чтобы пользователь john был владельцем накопителей на гибких дисках. Для этого вы можете использовать следующее правило: | Ключевое слово ''OWNER'', быть может и менее полезно, но позволяет вам задать имя Unix-пользователя, который будет владельцем узла устройства. Предположим, что мы столкнулись с несколько необычной ситуацией, когда вам надо, чтобы пользователь ''john'' был владельцем накопителей на гибких дисках. Для этого вы можете использовать следующее правило: | ||
KERNEL=="fd[0-9]*", OWNER="john" | |||
udev по умолчанию создаёт узлы с Unix-правами 0660 (разрешено читать/писать владельцу и группе). Если требуется, вы можете заменить эти умолчания для конкретных устройств, используя правила с оператором присваивания MODE . Для примера, следующее правило определяет, что узел inotify должен быть доступен всем на чтение и запись: | udev по умолчанию создаёт узлы с Unix-правами 0660 (разрешено читать/писать владельцу и группе). Если требуется, вы можете заменить эти умолчания для конкретных устройств, используя правила с оператором присваивания ''MODE''. Для примера, следующее правило определяет, что узел inotify должен быть доступен всем на чтение и запись: | ||
KERNEL=="inotify", NAME="misc/%k", SYMLINK+="%k", MODE="0666" | |||
==Использование внешних программ для присвоения имён устройствам.== | ==Использование внешних программ для присвоения имён устройствам.== | ||
В некоторых случаях вам может потребоваться большая гибкость, чем позволяют вам стандартные правила udev. В таком случае вы можете попросить udev запустить внешнюю программу и использовать стандартный вывод из этой для присвоения имени устройству. | В некоторых случаях вам может потребоваться большая гибкость, чем позволяют вам стандартные правила udev. В таком случае вы можете попросить udev запустить внешнюю программу и использовать стандартный вывод из этой для присвоения имени устройству. | ||
Для того, чтобы использовать эту возможность, вы просто указываете абсолютный путь к запускаемой программе (и любые другие параметры) в операторе присвоения PROGRAM, и затем используете какой-то из вариантов подстановки %c | Для того, чтобы использовать эту возможность, вы просто указываете абсолютный путь к запускаемой программе (и любые другие параметры) в операторе присвоения ''PROGRAM'', и затем используете какой-то из вариантов подстановки ''%c'' в операторах присвоения NAME/SYMLINK. | ||
Следующие примеры используют фиктивную программу /bin/device_namer. | Следующие примеры используют фиктивную программу ''/bin/device_namer''. device_namer использует один аргумент - имя устройства из ядра. device_namer обрабатывает это имя устройства и выдаёт результат в ''stdout''. Результат состоит из нескольких частей. Каждая часть - это отдельное слово. Части отделены друг от друга одним пробелом. | ||
В первом примере предполагается, что device_namer выдаёт несколько частей, каждая из которых создаёт симлинк (альтернативное имя) для устройства из запроса к программе. | В первом примере предполагается, что device_namer выдаёт несколько частей, каждая из которых создаёт симлинк (альтернативное имя) для устройства из запроса к программе. | ||
KERNEL=="hda", PROGRAM="/bin/device_namer %k", SYMLINK+="%c" | |||
Следующий пример предполагает, что device_namer выдаёт два слова, первое - имя устройства, второе имя - для симлинка. Здесь мы показываем использование подстановки %c{N}, которая указывает на часть с номером N в выводе программы: | Следующий пример предполагает, что 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, ... и так до конечного слова из вывода программы. | Следующий пример предполагает, что 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-группа, которой должно принадлежать устройство: | Выходные слова можно использовать с любым присваивающим ключевым словом, а не только с NAME и SYMLINK. Приводимый далее пример использует фиктивную программу, с помощью которой определяется Unix-группа, которой должно принадлежать устройство: | ||
KERNEL=="hda", PROGRAM="/bin/who_owns_device %k", GROUP="%c" | |||
==Запуск внешних программ при определённых событиях.== | ==Запуск внешних программ при определённых событиях.== | ||
Дополнительной причиной написания правил udev может послужить необходимость в запуске специальной программы в моменты, когда устройство подключают к системе или отключают. Например, вам хочется выполнить скрипт автоматического скачивания ваших фотографий с камеры после того, как камеру вы подключили к компьютеру. | Дополнительной причиной написания правил udev может послужить необходимость в запуске специальной программы в моменты, когда устройство подключают к системе или отключают. Например, вам хочется выполнить скрипт автоматического скачивания ваших фотографий с камеры после того, как камеру вы подключили к компьютеру. | ||
Вас не должны вводить в заблуждение ранее описанные возможности PROGRAM. PROGRAM используется для запуска программ которые выдают имена устройств (и эти программы ничего другого делать и не должны). В момент, когда эти программы выполняются, узел устройства ещё не создан, так что работать с устройством в этот момент никак не получится. | Вас не должны вводить в заблуждение ранее описанные возможности ''PROGRAM''. ''PROGRAM'' используется для запуска программ которые выдают имена устройств (и эти программы ничего другого делать и не должны). В момент, когда эти программы выполняются, узел устройства ещё не создан, так что работать с устройством в этот момент никак не получится. | ||
Далее описанная функциональность позволяет запустить программу уже после того, как узел устройства уже создан. В этом случае программа может работать с устройством, однако она не должна делать это долго, поскольку udev приостанавливается на время пока такая программа работает. Обойти это ограничение можно тем, что программа должна после запуска сразу отсоединиться. | Далее описанная функциональность позволяет запустить программу уже после того, как узел устройства уже создан. В этом случае программа может работать с устройством, однако она не должна делать это долго, поскольку udev приостанавливается на время пока такая программа работает. Обойти это ограничение можно тем, что программа должна после запуска сразу отсоединиться. | ||
Вот пример правила, которое демонстрирует оператор присвоения RUN, добавляющий указанную программу в список запускаемых: | Вот пример правила, которое демонстрирует оператор присвоения ''RUN'', добавляющий указанную программу в список запускаемых: | ||
KERNEL=="sdb", RUN+="/usr/bin/my_program" | |||
Когда выполняется /usr/bin/my_program, некоторые переменные udev доступны через переменные окружения, включая такие значения, как SUBSYSTEM. Вы можете также использовать переменную окружения ACTION, чтобы определить было ли устройство подключено или отключено - значение ACTION будет или "add" или "remove" соответственно. | Когда выполняется ''/usr/bin/my_program'', некоторые переменные udev доступны через переменные окружения, включая такие значения, как ''SUBSYSTEM''. Вы можете также использовать переменную окружения ''ACTION'', чтобы определить было ли устройство подключено или отключено - значение ACTION будет или "add" или "remove" соответственно. | ||
udev не запускает такие программы на каком-нибудь активном терминале, и не выполняет их через shell. Удостоверьтесь, что файлы программ имеют аттрибут исполняемости, а если это shell-скрипт, то удостоверьтесь, что первой строкой в скрипте указан правильный shell (т.е. #!/bin/sh), и не надейтесь увидеть на своём терминале то, что выводит программа в stdout. | udev не запускает такие программы на каком-нибудь активном терминале, и не выполняет их через shell. Удостоверьтесь, что файлы программ имеют аттрибут исполняемости, а если это shell-скрипт, то удостоверьтесь, что первой строкой в скрипте указан правильный shell (т.е. #!/bin/sh), и не надейтесь увидеть на своём терминале то, что выводит программа в stdout. | ||
==Взаимодействие с окружением== | ==Взаимодействие с окружением== | ||
udev позволяет использовать ключевое слово ENV key для получения значений из окружения как для поиска соответствия, так и для присваивания. | udev позволяет использовать ключевое слово ''ENV'' key для получения значений из окружения как для поиска соответствия, так и для присваивания. | ||
В случае присваивания, вы можете устанавливать переменные окружения, по содержимому которых вы позже можете проверить соответствие. Вы также можете устанавливать переменнные окружения и использовать из в любой внешней программе, которую запускаете при помощи ранее описанной техники. Ниже показан выдуманный пример правила, которое устанавливает переменную окружения. | В случае присваивания, вы можете устанавливать переменные окружения, по содержимому которых вы позже можете проверить соответствие. Вы также можете устанавливать переменнные окружения и использовать из в любой внешней программе, которую запускаете при помощи ранее описанной техники. Ниже показан выдуманный пример правила, которое устанавливает переменную окружения. | ||
KERNEL=="fd0", SYMLINK+="floppy", ENV{some_var}="value" | |||
В случае поиска соответствия , вы можете заставить срабатыват правило в зависимости от значения внешней переменной. Замечу, что окружение udev вовсе те то, что имеет пользователь за своей консолью. Ниже показано выдуманное правило, которое осуществляет поиск соответствия с переменной окружения. | В случае поиска соответствия , вы можете заставить срабатыват правило в зависимости от значения внешней переменной. Замечу, что окружение udev вовсе те то, что имеет пользователь за своей консолью. Ниже показано выдуманное правило, которое осуществляет поиск соответствия с переменной окружения. | ||
KERNEL=="fd0", ENV{an_env_var}=="yes", SYMLINK+="floppy" | |||
Это правило создаст ссылку /dev/floppy только если $an_env_var будет иметь значение "yes" в окружении udev's. | Это правило создаст ссылку ''/dev/floppy'' только если $an_env_var будет иметь значение "yes" в окружении udev's. | ||
==Дополнительные опции== | ==Дополнительные опции== | ||
Ещё одно присваивание может оказаться полезным. Это список OPTIONS. Доступно несколько опций: | Ещё одно присваивание может оказаться полезным. Это список ''OPTIONS''. Доступно несколько опций: | ||
*'''all_partitions''' - создать все возможные разделы для блочного устройства, а не только те, что были найдены изначально | |||
*'''ignore_device''' - игнорировать события полностью | |||
*'''last_rule''' - гарантировать, что никакие более поздние правила применяться не будут | |||
Например, следующее правило устанавливает принадлежность к группе для моего жёсткого диска, и гарантирует, что последующие правила применяться не будут: | Например, следующее правило устанавливает принадлежность к группе для моего жёсткого диска, и гарантирует, что последующие правила применяться не будут: | ||
KERNEL=="sda", GROUP="disk", OPTIONS+="last_rule" | |||
=Примеры= | =Примеры= | ||
Строка 398: | Строка 370: | ||
==USB Принтер== | ==USB Принтер== | ||
Я включил свой принтер и узел устройства получил имя /dev/lp0. Это имя меня не устраивает, и я решил использовать udevinfo, чтобы написать правило, создающее другое имя: | Я включил свой принтер и узел устройства получил имя ''/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 камера== | ||
Строка 424: | Строка 394: | ||
Не все камеры так работают: некоторые из них используют протокол, который не представляет их как устройства хранения данных. Подобные камеры поддерживаются программой gphoto2. В случаях, когда с камерой работаешь через gphoto, вам не надо писать правила для устройства, поскольку оно полностью управляется из пространства пользователя (а не специальным драйвером ядра). | Не все камеры так работают: некоторые из них используют протокол, который не представляет их как устройства хранения данных. Подобные камеры поддерживаются программой gphoto2. В случаях, когда с камерой работаешь через gphoto, вам не надо писать правила для устройства, поскольку оно полностью управляется из пространства пользователя (а не специальным драйвером ядра). | ||
Сложностью работы с устройствами USB камеры является то, что она обычно показывает себя как диск с одним разделом, например как /dev/sdb с разделом /dev/sdb1. Узел sdb для меня бесполезен, но вот sdb1 нужен - это раздел, который хочу смонтировать. Поскольку в sysfs данные представлены в виде цепочки, нужные мне | Сложностью работы с устройствами USB камеры является то, что она обычно показывает себя как диск с одним разделом, например как ''/dev/sdb'' с разделом ''/dev/sdb1''. Узел sdb для меня бесполезен, но вот sdb1 нужен - это раздел, который хочу смонтировать. Поскольку в sysfs данные представлены в виде цепочки, нужные мне атрибуты, которые udevinfo выдаёт для /dev/sdb1 полностью совпадают в выводом для /dev/sdb. Такие результаты в вашем правиле потенциально подходят как для всего диска, так и для раздела. И это не то, что вам хочется, так что ваше правило должно быть '''специфично'''. | ||
Чтобы обойти проблему, надо подумать, в чём разница между sdb и sdb1. Ответ удивительно прост: имена отличаются, так что мы можем использовать простой образец, который бы соответствовал полю имени устройства: | Чтобы обойти проблему, надо подумать, в чём разница между 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 диск, | И конечно, когда у вас 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 кардридер== | ||
USB кардридеры (CompactFlash, SmartMedia, etc) это совсем другой диапазон USB устройств хранения, и при их использовании есть другие требования. | USB кардридеры (CompactFlash, SmartMedia, etc) это совсем другой диапазон USB устройств хранения, и при их использовании есть другие требования. | ||
Эти устройства обычно не информируют компьютер о смене носителя. Так, если вы вставили устройство без носителя | Эти устройства обычно не информируют компьютер о смене носителя. Так, если вы вставили устройство без носителя данных, а затем вставили в него карту памяти, компьютер об этом не узнает, и у вас не будет узла sdb1 для раздела, который вы могли бы смонтировать. | ||
Одно из возможных решений - использовать возможности опции all_partitions, которая | Одно из возможных решений - использовать возможности опции ''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. | У вас теперь появятся узлы устройств с именами: cfrdr, cfrdr1, cfrdr2, cfrdr3, ..., cfrdr15. | ||
==USB Palm Pilot== | ==USB Palm Pilot== | ||
Эти устройства работают как USB-serial, так что по умолчанию у вас будет только узел устройства ttyUSB1. Утилиты palm работают с /dev/pilot, так что, многие юзеры хотели бы использовать правило, которое обеспечит создание /dev/pilot. | Эти устройства работают как USB-serial, так что по умолчанию у вас будет только узел устройства ''ttyUSB1''. Утилиты palm работают с ''/dev/pilot'', так что, многие юзеры хотели бы использовать правило, которое обеспечит создание ''/dev/pilot''. | ||
Carsten Clasohm's blog post содержит то, как это сделать. Правило от Carsten-а представлено ниже: | [http://www.clasohm.com/blog/one-entry?entry%5fid=12096 Carsten Clasohm's blog post] содержит то, как это сделать. Правило от Carsten-а представлено ниже: | ||
SUBSYSTEMS=="usb", ATTRS{product}=="Palm Handheld", KERNEL=="ttyUSB*", SYMLINK+="pilot" | |||
Отмечу, что строка для продукта может меняться от продукта к продукту, так что проверьте (используйте udevinfo) какая подойдёт в вашем случае. | Отмечу, что строка для продукта может меняться от продукта к продукту, так что проверьте (используйте udevinfo) какая подойдёт в вашем случае. | ||
==CD/DVD приводы== | ==CD/DVD приводы== | ||
Строка 492: | Строка 456: | ||
Поскольку я знаю, как ядро именует эти устройства, правила очень просты. Вот пример для моей системы: | Поскольку я знаю, как ядро именует эти устройства, правила очень просты. Вот пример для моей системы: | ||
SUBSYSTEM=="block", KERNEL=="hdc", SYMLINK+="dvd", GROUP="cdrom" | |||
SUBSYSTEM=="block", KERNEL=="hdd", SYMLINK+="dvdrw", GROUP="cdrom" | |||
==Сетевой интерфейсы== | |||
Хотя к ним мы и обращаемся по имени, сетевые интерфейсы обычно не имеют узлов устройств, связанных с ними. Несмотря на это, процесс создания правил для них практически такой же. | Хотя к ним мы и обращаемся по имени, сетевые интерфейсы обычно не имеют узлов устройств, связанных с ними. Несмотря на это, процесс создания правил для них практически такой же. | ||
Строка 503: | Строка 465: | ||
Имеет смысл в правиле просто найти соответствие с MAC-адресом вашего интерфейса, поскольку MAC-адреса уникальны. Однако удостоверьтесь, что вы используете абсолютно тот MAC-адрес, который выдаёт udevinfo, поскольку если регистр букв не совпадает полностью, правило работать не будет. | Имеет смысл в правиле просто найти соответствие с 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 или подобных утилитах. | |||
Следует перегрузить драйвер для сетевой карты, чтобы правило сработало. Вы можете выгрузить и затем загрузить модуль или просто перезагрузить систему. Также вам потребуется изменить конфигурацию системы, чтобы она использовала "lan" а не "eth0". У меня были некоторые проблемы с | |||
=Тестирование и отладка= | =Тестирование и отладка= | ||
Строка 519: | Строка 480: | ||
==Ввод в действие ваших правил== | ==Ввод в действие ваших правил== | ||
Если у вас ядро из последних с поддержкой inotify, udev автоматически контролирует содержимое каталога с вашими правилами и автоматически применит любые модификации, которые вы сделали в файлах правил. | Если у вас ядро из последних с поддержкой ''inotify'', udev автоматически контролирует содержимое каталога с вашими правилами и автоматически применит любые модификации, которые вы сделали в файлах правил. | ||
Несмотря на сказанное, udev не будет автоматически заново обрабатывать все устройства и пытаться применить все | Несмотря на сказанное, udev не будет автоматически заново обрабатывать все устройства и пытаться применить все новые правила. Например, вы написали правило, которое делает дополнительный симлинк для вашей камеры, а камера уже была подключена к системе. В этом случае вам не стоит ожидать, что симлинк сразу появится. | ||
Чтобы он появился, вам надо отключить и заново подключить камеру. Альтернативой для непереподключаемых устройств служит запуск программы udevtrigger. | Чтобы он появился, вам надо отключить и заново подключить камеру. Альтернативой для непереподключаемых устройств служит запуск программы '''udevtrigger'''. | ||
Если ядро не поддерживает inotify, новые правила автоматически не определятся. В таком случае следует запустить udevcontrol reload_rules после изменения файлов с правилами. | Если ядро не поддерживает inotify, новые правила автоматически не определятся. В таком случае следует запустить '''udevcontrol reload_rules''' после изменения файлов с правилами. | ||
==udevtest== | ==udevtest== | ||
Если вы знаете верхнюю точку пути к устройству в sysfs, | Если вы знаете верхнюю точку пути к устройству в 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 - это чисто тестовое/отладочное средство, оно не создаёт узлов устройств, несмотря на содержимое того, что эта программа выводит на экран! | Отмечу, что префикс ''/sys'' был удалён из командной строки для udevtest. Это потому, что udevtest работает с путями к устройствам. Также замечу, что udevtest - это чисто тестовое/отладочное средство, оно не создаёт узлов устройств, несмотря на содержимое того, что эта программа выводит на экран! | ||
=Автор и контакты= | =Автор и контакты= | ||
Строка 552: | Строка 511: | ||
This document is licensed under the GNU General Public License, Version 2. | This document is licensed under the GNU General Public License, Version 2. | ||
Источники | =Источники= | ||
*[https://www.arccomm.ru/Правила-udev arccomm.ru] | |||
*[https://storaged.org/doc/udisks2-api/latest/mount_options.html storaged.org] | |||
{{Category navigation|title=Перевод man-ов|category=Перевод man-ов|sortkey={{SUBPAGENAME}}}} | {{Category navigation|title=Перевод man-ов|category=Перевод man-ов|sortkey={{SUBPAGENAME}}}} |
Текущая версия от 16:00, 13 ноября 2024
Как писать правила для 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 я могу использовать {{{1}}} для того, чтобы идентифицировать этот диск. 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 может послужить необходимость в запуске специальной программы в моменты, когда устройство подключают к системе или отключают. Например, вам хочется выполнить скрипт автоматического скачивания ваших фотографий с камеры после того, как камеру вы подключили к компьютеру.
Вас не должны вводить в заблуждение ранее описанные возможности PROGRAM. PROGRAM используется для запуска программ которые выдают имена устройств (и эти программы ничего другого делать и не должны). В момент, когда эти программы выполняются, узел устройства ещё не создан, так что работать с устройством в этот момент никак не получится.
Далее описанная функциональность позволяет запустить программу уже после того, как узел устройства уже создан. В этом случае программа может работать с устройством, однако она не должна делать это долго, поскольку 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.