Не дуже коротко про udev
Не дуже коротко про udev
kayo - Нд, 15/12/2013 - 00:44
Є така примудрена штука у цих нашихlinux -ах, якudev. Це сервіс, який підхоплює та конфігурує периферію, отримуючи повідомлення від ядра. Він гнучко налаштовується під обладнання та завдання за допомогою спеціальних правил. Стандартні системні правила зазвичай лежать у директорії /lib/udev/rules.d, а наших інвалідів ми можемо сміливо зберігати у /etc/udev/rules.d.
Правила дуже прості і зазвичай працюють в лоб без будь-яких викрутасів, однак, написати їх з першого разу без хорошого розуміння принципів пристроюsysfs ядра linux буває дуже непросто. У цьому ми зараз і розбиратимемося. Звичайно, документації та прикладів безліч, але вони, як правило, стосуються стандартних речей і тривіальних завдань. Ми ж робитимемо дещо, що виходить за рамки і з розумінням того, звідки що насправді береться.
Про sysfs (так ту саму, яка зазвичай монтується в / sys), можна розповісти на велику статтю, але тут ми торкнемося тільки того, що нам найчастіше може знадобитися. Отже, ось деякі важливі для нас підкаталоги:
- /bus - тут розташовані пристрої по шинах обміну даними (несподівано чи не так ^_^ ). Як правило, сюди дивитися нам не знадобиться, але матимемо на увазі.
- /class - Тут пристрої за класами. Це нам однозначно знадобиться.
- /Devices - пристрої за типами. Часто ядро кидає повідомлення з шляхами пристроїв тут, а з інших підкаталогів дивляться сюди символьні посилання.
- /kernel/debug - файлова система налагоджувального інтерфейсу ядра. Може стати в нагоді, щоб побачити, що не так з пристроями.
Маємо на увазі, що оскільки udev працює з sysfs,всі шляхи (syspath) слід розглядати щодо /sys, тобто замість /sys/devices/xyz писати /devices/xyz.
Корисний інструмент для роботи з udev, напевно, єдиний, без чого не можна обійтися при налаштуванні.
Досліджуємо sysfs командою info
Ось як ми можемо дізнатися, що udev думає про пристрій або підсистему, що цікавить нас, для прикладу візьмемоgpio наRaspberry Pi :
У цьому прикладі ми не отримали нічого цікавого, але зможемо дійти невтішного висновку, що це територія ядра. Записи виду ABCD=="abcd" це селектори, які можуть бути використані як умови у правилах. Підемо далі, досліджуємо контролер gpio:
Давайте подивимося щось інше, наприклад пристрійi2c :
Те, що ми тут бачимо є ієрархією вкладених один в одного вузлів від приватного до загального. Видавати таке бадилля, як ви вже здогадалися, змушує параметр --attribute-walk. Варто звернути увагу на шляхи та значення селекторів. У мене на цій шині сидить годинник реального часу, тому можемо зробити так:
Змінився тільки найнижчий (тобто верхній) вузол, тепер там власне сам годинник.
Ловимо події командою monitor
Часто буває корисно моніторити події ядра і udev, щоб зрозуміти, що відбувається, наприклад при додаванні або видаленні пристроїв, що цікавлять нас. Ми можемо запустити моніторинг так:
Тут ми ловимо всі події ядра і самого udev з усіма властивостями. Звичайно, можна вказати фільтри, але зазвичай і так у висновку легко побачити те, що нас цікавить. Запускаємо монітор, приєднуємо пристрій та дивимося, що ми маємо. Давайте для прикладу спробуємо проініціалізувати портgpio :
При цьому в моніторі буде приблизно таке:
Надійшло дві подіїдодавання пристрою: одне від ядра, інше згенерував сам udev. Тепер можна дослідити пристрій, що з'явився, як ми це вже робили:
Як бачимо, так, як і очікується. А тепер видалимо порт:
Буде все те саме, тільки дія видалення замість додавання:
Пробуємо писати правила
Отже, звідки береться ми більш-менш розібралися, настав час написати якісь корисні правила.
Допустимо, нам потрібно надати доступ до пристроїв gpio групи gpio. Створюємо файл /etc/udev/rules.d/90-gpio.rules, цифра за традицією – це пріоритет правила. Для керування gpio, тобто активації/деактивації портів служать файли /sys/class/gpio/export та /sys/class/gpio/unexport. Для керування напрямом роботи порту та отримання/установки значення використовуються файли /sys/class/gpio/gpioN/direction та /sys/class/gpio/gpioN/value відповідно. Все, що нам потрібно, перепризначити групу для всіх цих файлів gpio, а потім дати права на запис в них для групи. Ось мій варіант правил:
Перше правило налаштовує доступ до інтерфейсів export/unexport, а друге - до direction та value кожного настроєного порту. Як було зазначено вище, записи ABCD=="xyz" — це селектори, що задають умова спрацьовування правила. Правило діє, якщо всі умови дотримані. Записи ABCD="xyz" - це дії, тобто те, що буде виконано під час активації правила. У селекторах можна використовувати прості шаблони:
- «*» — будь-яка кількість символів, у тому числі й не одного
- "?" один або ні одного символу
- "[abcd]" - один із символів групи (можна задавати діапазони виду [a-z], [0-9] і тд)
Занадто багато від шаблонів не слід очікувати, це вам не регулярні висловлювання, проте функціонала цілком достатньо для всього. УУ наших правилах ми використовували шаблон [0-9]* для узагальнення імені KERNEL номером пристрою. Щоб інвертувати дію селектора, треба писати його як ABCD!="xyz".
У дії можна використовувати підстановки виду %xyz. Ось деякі з них:
- %k — ім'я KERNEL пристрою
- %n — номер KERNEL пристрою
- %p – значення DEVPATH
У наших правилах ми використовували %p, щоб не писати довге значення DEVPATH. Ми використовували дію виклику зовнішньої програми, в якому запустили оболонку, щоб виконати призначення групи та надати права доступу. Тут може виникнути думка, що запускати оболонку - дія надмірна і не обов'язкова, проте, udev не виконає більше однієї програми, двічі вказати PROGRAM в одному правилі не можна, тому що останнє присвоєння замінить перше. Хорошою ідеєю може бути написати правило двічі, але я не впевнений, чи це так насправді.
Раніше, коли я досліджував начинки sysfs, мені здавалося дивним величезна кількість символьних посилань у різних каталогах, які часом змушують слідувати довгими зацикленими ланцюжками. Вся їхня корисність стає очевидною коли починаєш писати правила udev. У нашому випадку ми використовували символьне посилання subsystem, щоб дійти інтерфейсів управління портами, хоча у разі приклад не дуже показовий.
Тестуємо правила
Щоб зрозуміти, чи не накосячили ми випадково десь, можемо зробити таке:
Тут ми форсуємо подію, а udev читає правила та виконує дії при збігу умов. Я виконав це під непривілейованим користувачем, тому я отримав ненульовий код повернення.
Щоб додані правила почали працювати, треба змусити демон їх перечитати:
Тепер додаємо пристрої, як ми це вже робили та дивимося, чи встановилися привілеї.
Давайте напишемо ще якихось правил. Як я казав, у нас є годинник реального часу на шині i2c, однак, система не в змозі правильно зв'язати пристрій годинника з шиною без додаткових правил. Логічно використовувати для цих цілей подію додавання пристрою i2c, що виникає при ініціалізації драйвера:
Правило для видалення тут приведено до купи, я не впевнений, що воно необхідне, оскільки коли драйвер i2c вивантажується, пов'язані пристрої автоматично видаляються.