Як виконати оболонку, коли я підключаю USB-пристрій


28

Я хочу виконати сценарій, коли я підключаю пристрій у своїй машині Linux. Наприклад, запустіть xinputмишу або резервну скрипт на певному диску.

Я про це бачив багато статей, останнім часом тут і тут . Але я просто не можу змусити його працювати.

Ось кілька простих прикладів, які намагаються отримати хоч якусь відповідь.

/etc/udev/rules.d/test.rules

#KERNEL=="sd*", ATTRS{vendor}=="*", ATTRS{model}=="*", ATTRS{serial}=="*", RUN+="/usr/local/bin/test.sh"
#KERNEL=="sd*", ACTION=="add", "SUBSYSTEM=="usb", ATTRS{model}=="My Book 1140    ", ATTRS{serial}=="0841752394756103457194857249", RUN+="/usr/local/bin/test.sh"
#ACTION=="add", "SUBSYSTEM=="usb", RUN+="/usr/local/bin/test.sh"
#KERNEL=="sd*", ACTION=={add}, RUN+="/usr/local/bin/test.sh"
KERNEL=="sd*", RUN+="/usr/local/bin/test.sh"
KERNEL=="*", RUN+="/usr/local/bin/test.sh"

/usr/local/bin/test.sh

#!/usr/bin/env bash
echo touched >> /var/log/test.log

if [ "${ACTION}" = "add" ] && [ -f "${DEVICE}" ]
then
    echo ${DEVICE} >> /var/log/test.log
fi

Папка правил переглядається inotifyі повинна бути активна негайно. Я постійно відключаю підключення клавіатури, миші, планшета, картки пам'яті та USB-накопичувача, але нічого. Жоден файл журналу не торкався.

Тепер, який би був найпростіший спосіб принаймні щось знати? Простіше працювати з чогось, що працює, ніж з того, що ні.


1
Ви не мали на увазі публікацію на Unix та Linux ? Яка версія вашого ядра? Ви запустили udevadm triggerчи підключили пристрій, щоб застосувати нове правило?
Жил "ТАК - перестань бути злим"

Так, я роблю це після кожного редагування правил, щоб випробувати їх. Я відповідно редагував питання. Ось так удев працює деякий час, але я біжу 3.5.0-23-generic.
Редсандро

Відповіді:


24

Якщо ви хочете запустити скрипт на певному пристрої, ви можете використовувати ідентифікатори постачальника та продукту

  • В /etc/udev/rules.d/test.rules:

    ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2329", RUN+="/tmp/test.sh"
  • в test.sh:

    #! /bin/sh
    
    env >>/tmp/test.log
    file "/sys${DEVPATH}" >>/tmp/test.log
    
    if [ "${ACTION}" = add -a -d "/sys${DEVPATH}" ]; then
    echo "add ${DEVPATH}" >>/tmp/test.log
    fi

З env , ви можете побачити, яке середовище встановлено з udev, і за допомогою якого file, ви дізнаєтесь тип файлу.

Конкретні атрибути вашого пристрою можна виявити за допомогою lsusb

lsusb

дає

...
Автобус 001 Пристрій 016: ID 152d: 2329 JMicron Technology Corp. / JMicron USA Technology Corp. JM20329 Міст SATA
...


1
Це цікаво! Здається, що він не має дозволу писати в / log /. Це робить запис в / TMP /. Я думаю, що не мав дозволу читати і мої попередні тестові сценарії.
Редсандро

@Redsandro Це було не навмисно, лише для, ну, тестування. У всякому разі, я радий, що це допомогло. ;-)
Олаф Дієше

Я хотів би закликати вас також перевірити це запитання і побачити, чи можуть ваші знання там бути цінними. :)
Редсандро

3
Ви також можете додати ACTION=="add",безпосередньо до визначення правила.
Avindra Goolcharan

4

Це стосується не вашого питання, а того, що ви робите. Якщо ви запустите скрипт резервного копіювання з udev, у вас виникнуть дві основні проблеми:

  1. Ваш scrpit може бути запущений до того, як пристрій буде готовий і його можна встановити, ви повинні зберегти стан KERNEL == "sd *", якщо ви хочете використовувати вузол / dev для його монтажу.
  2. Що ще важливіше, якщо ваш scirpt потребує певного часу для виконання (що може бути легким у випадку з резервним сценарієм), він буде вбитий незабаром після його запуску (близько 5 с)
  3. Ви зіткнетеся з багатьма складними проблемами з дозволом користувача

Моя порада - створити в домашньому користувачеві сценарій, який прослухає названу трубку, яка буде запущена асинхронно, як:

#!/bin/bash

PIPE="/tmp/IomegaUsbPipe"
REMOTE_PATH="/path/to/mount/point"
LOCAL_PATH="/local/path/"


doSynchronization()
{
  #your backup here
}

trap "rm -f $PIPE" EXIT

#If the pipe doesn't exists, create it
if [[ ! -p $PIPE ]]; then
    mkfifo $PIPE
fi

#If the disk is already plugged on startup, do a syn
if [[ -e "$REMOTE_PATH" ]]
then
    doSynchronization
fi

#Make the permanent loop to watch the usb connection
while true
do
    if read line <$PIPE; then
        #Test the message red from the fifo
        if [[ "$line" == "connected" ]]
        then
            #The usb has been plugged, wait for disk to be mounted by KDE
            while [[ ! -e "$REMOTE_PATH" ]]
            do
                sleep 1
            done
            doSynchronization
        else
            echo "Unhandled message frome fifo : [$line]"
        fi
    fi
done
echo "Reader exiting"

Примітка: я використовую автоматичне кріплення з kde, тому я перевіряю, чи з’являється папка. Ви можете передати параметр / dev / sd * у fifo з правила udev і встановити його самостійно в сценарії. Щоб написати у фіфо, не забувайте, що udev - це не оболонка, і що перенаправлення не працює. Ваш RUN має бути таким:

RUN + = "/ bin / sh -c '/ bin / echo підключено >> / tmp / IomegaUsbPipe'"


Велике використання названих труб тут. Мені було цікаво, що ви також можете просто створити довільний файл в tmp і шукати його, а не названу трубку, правильно?
jamescampbell

1

Я розмістив рішення на /ubuntu//a/516336 і я також копіюю рішення для скріплення.

Я написав сценарій Python, використовуючи pyudev, який залишаю працювати у фоновому режимі. Цей сценарій прослуховує події udev (таким чином, це дуже ефективно) і працює будь-який код, який я хочу. У моєму випадку він виконує xinputкоманди для установки моїх пристроїв ( посилання на останню версію ).

Ось коротка версія того ж сценарію:

#!/usr/bin/env python3

import pyudev
import subprocess

def main():
    context = pyudev.Context()
    monitor = pyudev.Monitor.from_netlink(context)
    monitor.filter_by(subsystem='usb')
    monitor.start()

    for device in iter(monitor.poll, None):
        # I can add more logic here, to run different scripts for different devices.
        subprocess.call(['/home/foo/foobar.sh', '--foo', '--bar'])

if __name__ == '__main__':
    main()

1
Схоже, хороший сценарій, +1. Я б запропонував використовувати список, а не лише один рядок в call(). Таким чином, якщо необхідно надати аргументи foobar.shсценарію, ви можете це зробити динамічно.
Сергій Колодяжний

1
Справедлива точка. Мій "справжній" сценарій (пов'язаний з відповіддю) використовує список. У цій мінімалістичній версії, яку я встав тут, я заплутався і випадково використав рядок. Спасибі! Я оновив відповідь.
Denilson Sá Maia

-1

Щоб запустити скрипт під час завантаження, коли вставлено пристрій usb, я використовую рішення нижче:

Відформатуйте pendrive або будь-яке інше сховище usb та дайте йому ім’я, виконуючи це. Потім у /etc/rc.local рядку додаванняls -q /dev/disk/by-label > /home/pi/label.txt

він створить файл txt під назвою label.txt (може бути будь-яке інше ім’я)

потім знову в /etc/rc.local додайте ще 2 рядки:

if  grep -q USB_drive_name /home/pi/label.txt; then
sudo /home/pi/script.sh

Тепер кожного разу, коли буде вставлено pendrive з ім'ям USB_drive_name, він запустить сценарій.

За допомогою декількох невеликих модифікацій вище рішення можна використовувати, коли система працює.


Не відповідає на питання: Це стосується лише часу завантаження (а використання udevінших часів не є «кількома невеликими модифікаціями») та Raspberry Pi. Існує непотрібне sudo- rc.localзапускається як root, це проблема ескалації привілеїв - файл, який можна редагувати звичайним користувачем, запускається як root.
Герт ван ден Берг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.