Pautomount — демон автоматичного монтування, запуску скриптів і таке інше

Виникло переді мною завдання - автоматично виконувати дію при вставці якогось storage device в Дебіані. Наприклад, легко автоматично монтувати його. А може, монтувати та синхронізувати дані, якщо пристрій відомий. А може, перевіряти його clamav на всяку фігню і запускати на ньому щось типу USB-вакцини. Може, і включити сирену, якщо власника поруч із комп'ютером немає =)
Наприклад, є в мене флешки, штук так 5-7. На кожній записано щось своє, одна — завантажувальна, одна з документами, одна з програмами, одна з музикою та з фотками… І стоїть вдома сервер, на якому всі копії того, що на флешках, є. Хотілося б мені, щоб із однією флешкою синхронізувалися документи, з іншого — музика та фотки, з третьої — завантажувальні образи тощо. Тільки ось потрібно цю справу якось автоматично запускати, тому що не годиться щоразу через SSH на сервер лізти та ручками все правити. Тому потрібно щось, де можна було б флешки прописати та дії, потрібні при підключенні, поставити. А там один раз скрипт синхронізації написав і готово.
Тільки ось нічого готового і цілком придатного в Гуглі не знайшлося. Частина рішень тягнуть за собою купу залежностей і не розраховані на headless-установку, частина застаріли, частина не мають всього необхідного функціоналу, а частина дозволяють це зробити, тільки ось надто мотузно, налаштовувати важко, і взагалі, NIH ;-) Тому написав я свій демон автоматичного монтування, та й викладаю його сюди, може, знадобиться комусь ще, може, такому ж власнику домашнього сервера, який так само хоче синхронізувати флешки з домашнім NAS. Та й хочеться, щоби хтось код покритикував, на помилки вказав і вирішення деяких проблем підказав — дивитися наприкінці топіка.
Був у мене якось iPod. Іноді виникала необхідність заряджати його від комп'ютера з Лінуксом. Але як тільки підключаєш його до комп'ютера, він одразу включає режим синхронізації. Для того, щоб він припинив заряджатися, потрібно або хардварне рішення (перехідник тато-мама USB з замкнутими контактами), або виконати eject /dev/sdx1, що вимикає режим синхронізації і переводить в режим заряджання. Відповідно, потрібно виконувати цю команду щоразу — паяти перехідник так ліньки… тоді й народився такий скрипт — umipod.sh: (UnMount-iPOD)
Прошу сильно не плюватися на якість коду, це було написано два роки тому, та й далі в статті буде ще багато коду – бережіть слину =) Суть проста – у циклі перевіряємо, чи підключений пристрій із конкретним UU > А зараз зіткнувся з деякими завданнями, для яких потрібні автоматичні дії під час підключення носія. Ось і вирішив це виправити, тільки тепер і близько не підходячи до Bash =) Мова програмування довго вибирати не довелося - та й нема з чого, чесно кажучи, я тільки Python останнім часом і займаюся. Далі довелося подумати — чи писати демон, який у циклі перевіряє, чи не з'явилося ще нових необроблених розділів, чи чіплятися через udev, щоб програма викликалася як callback при підключенні будь-якої флешки. Другий варіант начебто споживає менше ресурсів — але перший не залежить від udev і простіше. Тому вирішив писати демона, якого можна спокійно засунути в автозапуск.
Власне, ось він. Про пристрій і можливості найлегше розповісти, описуючи те, як його налаштовувати. Але все ж таки коротко опишу принцип роботи:
- Демон кожні n секунд перевіряє /dev/disk/by-uuid, потім за даними звідти складає список доступних розділів
- Цей перелікпорівнюється зі списком, зробленим за n секунд до цього, визначаються розділи, яких раніше не було
- За наявності свіжопідключених розділів за записами з конфіга визначається, який розділ проігнорувати, а для якого виконати якусь дію.
- GOTO 1
Далі вже вимагає опису конфіга =) Конфіг зазвичай складається з чотирьох секцій, будь-яку з яких можна безболісно пропустити.
1) Секція "globals"
Тут все просто - будь-яка змінна в цій секції експортується в global namespace демона такою невигадливою функцією:
Просто і швидко, не потрібно складати будь-які списки змінних до експорту або запихати всі змінні до словника.
Корисні змінні:main_mount_dir- основна директорія для монтування, коли шлях для монтування невідомий або вказаний у вигляді відносного шляху. За замовчуванням - "/media".default_mount_option— налаштування за замовчуванням, коли інші не вказані. Я залишаю там "rw", хоч і не впевнений, чи не прописано це вже за замовчуванням у драйверах для файлових систем =)logfile- шлях до лог-файлу. Якщо треба поміняти, краще вказувати абсолютний - чорт його знає, куди потраплять логи, якщо вказати відносний =) Швидше за все - \.interval— інтервал між перевірками щодо появи нових розділів. Як на мене — навіть інтервал в одну секунду не завантажує процесор значно, навіть на одноядерному Celeron 900MHz =)
Не настільки корисні змінні:debug- тут все зрозуміло. Значно прискорює збільшення розміру лог-файлу, в реальному житті навряд чи знадобиться =)super_debug- прискорює розширення лог-файлу ще більше. Імовірність того, що знадобиться ще менше.noexecute— опція, яка включена за замовчуванням і за якої демон тільки імітує виклики зовнішніх команд — таким чином, з моменту першого запуску і доти, доки не буде прибрано опцію, нічого монтуватися/виконуватися не буде. Зроблено для того, щоб до налаштування демон не монтував розділи, які вже монтуються за допомогою fstab.
2) Секція "exceptions"
Будь-який розділ, чий UUID/Label вказаний у цій секції, буде жорстоко проігноровано. Власне, основний use case - розділи, які автоматично монтуються під час завантаження системи з fstab. Тобто звичайні записи у цій секції виглядатимуть так:
3) Секція "rules"
Тут описуються правила для особливих випадків. Наприклад, тут можна вказати правило типу:
За наявності такого правила розділ UU >
4) Секція "default"
Тут все просто — якщо розділ не відповідає записам у двох попередніх секціях, то ця секція відповідає за дію за умовчанням. Зазвичай містить просто "mount": true, ну або "mount": false.
Як бачимо, в останніх трьох секціях в правилах є два типи змінних. Перший тип - змінні для ідентифікації якогось конкретного розділу. Їх поки що три типи - "uuid", "label" і "label_regex".
- «uuid»- тут все зрозуміло.
- «label»- дозволяє задати мітку розділу, при збігу якої буде дія.
- «label_regex»— зіставляє мітку диска із зазначеним регулярним виразом, використовуючи re.match(). На вході, природно, очікується Perl-like регулярний вираз.
- «mount»може мати такі значення: true (автоматично монтувати), false (не монтувати) абословник із додатковими аргументами для монтування (мається на увазі true). Поки що допустимі ключі для словника - "mountpoint" (завдання точки монтування) та "options" (додаткові опції для монтування).
- «command»— рядок, що містить шлях до команди, яку слід виконати. Тут все ясно =)
- «script»— якийсь кастомний скрипт, який викликається з аргументами DEVICE_PATH UUID MOUNTPOINT LABEL. Замість останніх двох, якщо немає точки монтування або позначки розділу, буде None.
Додам пару прикладів правил у конфізі:
У результаті, що дозволяє цей демон? Крім того, що він допомагає автоматично монтувати розділи на пристроях, що підключаються, він може бути використаний для:
- Синхронізація файлів на знімних носіях з, наприклад, домашнім сервером на Linux
- Своєрідний usb-modeswitch, згадати ту ж ситуацію з iPod
- Перевірки носіїв на віруси
- Зливання даних з чиїхось iPhone/Andro > Зараз, коли все написано і повністю працює, але до кінця ще не відполіровано, у мене є кілька питань щодо поліпшення безпеки і відповідності яким-небудь традиціям, що там виробилися за багато років в написання демонів.
GitHub До речі, цю статтю можна вважати продовженням мною обіцяного, але вже майже рік як зупиненого курсу статей з налаштування свого переносного сервера, використовуючи Debian. Збирався зробити щось таке в рамках реалізації свого файлового сервера, тільки тоді не зміг знайти готового рішення… А зараз написав своє =) ВНайближчим часом думаю написати ще пару статей, які підійдуть під тематику цього курсу.
Хардкорна конфа за С++. Ми запрошуємо лише профі.