Автоматичне управління USB-накопичувачами з системою


27

Ми оновлюємо наші сервери від застарілого дистрибутива до сучасної системи на базі Debian Jessie, включаючи lightdm / xfce, і звичайно systemd (і udisks2). Однією з важливих моментів є автоматичне управління USB-накопичувачами. Для цього ми використовували деякі правила udev. Старі правила майже все ще працюють - створюється точка монтажу, а диск накопичується нормально, але через кілька секунд systemd робить щось, що порушує монтування, тому наступні спроби доступу призводять до помилок "Транспортна кінцева точка не підключена".

Ручне встановлення накопичувача за допомогою командного рядка працює добре. Так само дозволяє випускати файловий менеджер (thunar і thunar-volman, який, у свою чергу, використовує udisks2). Але це нежиттєздатні варіанти - ці системи в основному працюють без голови, тому тунар зазвичай не працює. Нам потрібно мати можливість підключити дискові накопичувачі для автономних резервних копій без нагляду.

Я подумав, що зміна сценарію udev для нерестування окремої роботи, яка чекає кілька секунд, перш ніж виконати кріплення, може зробити трюк, але systemd, здається, виходить зі свого шляху, щоб не допустити цього - воно якось ще чекає, коли відокремлена робота закінчиться раніше продовження.

Можливо, наявність сценарію udev галочку udisks2 якось є правильним підходом? Я в розгубі, тому будь-яку пораду високо оцінив.


1
Тільки дотично пов'язані, але ... Ви ставите xfce на сервер?
Парфянський розстріл

Ах, я використовував термін "сервер" досить вільно ... Вся взаємодія користувачів із системою відбувається через веб-додаток, як правило, доступ до нього здійснюється через браузер через мережу. Але деякі клієнти віддають перевагу немережевому рішення, тому ми запускаємо Chrome на консолі у своєрідному режимі кіоску. (Це також зручно для налагодження проблем з мережевою конфігурацією. Ви можете підключити монітор / мишу / клавіатуру та отримати доступ до основних інструментів діагностики у веб-додатку, не потребуючи даних для входу в Linux). Мабуть, є легше рішення ваги, ніж lightdm / xfce, але це було найпростіше налаштувати ...
Майк Блеквелл

Для всіх, хто хоче правила systemd-udevd, безпосередньо виконуючи сценарій: у мене це було; він працював деякий час, але в якийсь момент припинив виконання сценарію, якщо udevd був запущений автоматично. Зупиніть і перезапустіть з командного рядка, і це було б добре. Крім того, він ніколи не працював добре з NTFS + FUSE, оскільки udev виявив, що у нього тривалий процес дитини (ntfs-3g), і вбив його після 60-х років. Підсумок: правила udev безпосередньо запускаючи сценарій - це марна трата часу. Замість цього перейдіть із правилами udev та системною службою, як зазначено у відповідях. Тоді вам також не доведеться мати справу з просторами імен (MountFlags = slave).
Марк

У мене був аналогічний випуск сценарію, запущений udev, який не мав можливості встановити мережеві з'єднання. Рішення нижче щодо використання systemd працювало і на це - дякую!
Квентін Стаффорд-Фрейзер

Відповіді:


28

Після кількох помилкових стартів я зрозумів це. Ключ полягає в тому, щоб додати службу системного блоку між udev та монтажним скриптом.

(Для запису, я не зміг змусити це працювати за допомогою udisks2 (через щось на зразок udisksctl mount -b /dev/sdb1), викликаного безпосередньо з правила udev або з файлу системного блоку. Здається, є умова гонки, і вузол пристрою не зовсім готовий , в результаті чого Error looking up object for device /dev/sdb1. Невдало, оскільки udisks2 може піклуватися про всю безладність точки монтажу ...)

Важкий підйом виконується за допомогою сценарію оболонки, який піклується про створення та видалення точок кріплення, а також для монтажу та демонтажу приводів.

/usr/local/bin/usb-mount.sh

#!/bin/bash

# This script is called from our systemd unit file to mount or unmount
# a USB drive.

usage()
{
    echo "Usage: $0 {add|remove} device_name (e.g. sdb1)"
    exit 1
}

if [[ $# -ne 2 ]]; then
    usage
fi

ACTION=$1
DEVBASE=$2
DEVICE="/dev/${DEVBASE}"

# See if this drive is already mounted, and if so where
MOUNT_POINT=$(/bin/mount | /bin/grep ${DEVICE} | /usr/bin/awk '{ print $3 }')

do_mount()
{
    if [[ -n ${MOUNT_POINT} ]]; then
        echo "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}"
        exit 1
    fi

    # Get info for this drive: $ID_FS_LABEL, $ID_FS_UUID, and $ID_FS_TYPE
    eval $(/sbin/blkid -o udev ${DEVICE})

    # Figure out a mount point to use
    LABEL=${ID_FS_LABEL}
    if [[ -z "${LABEL}" ]]; then
        LABEL=${DEVBASE}
    elif /bin/grep -q " /media/${LABEL} " /etc/mtab; then
        # Already in use, make a unique one
        LABEL+="-${DEVBASE}"
    fi
    MOUNT_POINT="/media/${LABEL}"

    echo "Mount point: ${MOUNT_POINT}"

    /bin/mkdir -p ${MOUNT_POINT}

    # Global mount options
    OPTS="rw,relatime"

    # File system type specific mount options
    if [[ ${ID_FS_TYPE} == "vfat" ]]; then
        OPTS+=",users,gid=100,umask=000,shortname=mixed,utf8=1,flush"
    fi

    if ! /bin/mount -o ${OPTS} ${DEVICE} ${MOUNT_POINT}; then
        echo "Error mounting ${DEVICE} (status = $?)"
        /bin/rmdir ${MOUNT_POINT}
        exit 1
    fi

    echo "**** Mounted ${DEVICE} at ${MOUNT_POINT} ****"
}

do_unmount()
{
    if [[ -z ${MOUNT_POINT} ]]; then
        echo "Warning: ${DEVICE} is not mounted"
    else
        /bin/umount -l ${DEVICE}
        echo "**** Unmounted ${DEVICE}"
    fi

    # Delete all empty dirs in /media that aren't being used as mount
    # points. This is kind of overkill, but if the drive was unmounted
    # prior to removal we no longer know its mount point, and we don't
    # want to leave it orphaned...
    for f in /media/* ; do
        if [[ -n $(/usr/bin/find "$f" -maxdepth 0 -type d -empty) ]]; then
            if ! /bin/grep -q " $f " /etc/mtab; then
                echo "**** Removing mount point $f"
                /bin/rmdir "$f"
            fi
        fi
    done
}

case "${ACTION}" in
    add)
        do_mount
        ;;
    remove)
        do_unmount
        ;;
    *)
        usage
        ;;
esac

Сценарій, у свою чергу, викликається файлом системного блоку. Ми використовуємо синтаксис імен файлів "@", щоб ми могли передавати ім'я пристрою як аргумент.

/etc/systemd/system/usb-mount@.service

[Unit]
Description=Mount USB Drive on %i
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/local/bin/usb-mount.sh add %i
ExecStop=/usr/local/bin/usb-mount.sh remove %i

Нарешті, деякі правила udev запускають та зупиняють службу системного блоку на гарячій підключенні / відключенні вилки:

/etc/udev/rules.d/99-local.rules

KERNEL=="sd[a-z][0-9]", SUBSYSTEMS=="usb", ACTION=="add", RUN+="/bin/systemctl start usb-mount@%k.service"

KERNEL=="sd[a-z][0-9]", SUBSYSTEMS=="usb", ACTION=="remove", RUN+="/bin/systemctl stop usb-mount@%k.service"

Це, здається, робить трюк! Кілька корисних команд для налагодження таких матеріалів:

  • udevadm control -l debugвмикає багатослівний журнал, щоб /var/log/syslogви могли бачити, що відбувається.
  • udevadm control --reload-rules після того як ви зміните файли в dirls rules.d (можливо, це не буде потрібно, але не може зашкодити ...).
  • systemctl daemon-reload після зміни файлів системних одиниць.

4
Ого. Це круто. Бажаю, я міг би дати кілька відгуків! Єдине, що мені довелося змінити, це те, що в моїй системі, blkidздається, не витягується ID_FS_LABEL, тому я просто використовував, DEVBASEа не LABELбудував MOUNT_POINTзамість цього.
Тревіс Гріггс

Чи можна це налаштування змінити для роботи з пристроями ATA / SCSI? Дивіться: serverfault.com/q/825779/297059
користувач339676

@Travis - Ви можете використовувати udevadmзамість blkid. Це дає набагато більше деталей, а також додаткову інформацію. (наприклад, udevadm info --query=property --name=sda1)
користувач339676

це не добре працює під час завантаження, якщо пристрій USB вже підключено. Якісь ідеї?
Міхал Артазов

Якщо nullglobs не встановлено, під час відключення очищення може створити помилку /usr/bin/find: '/media/*': No such file or directory. Очищення може використовувати додатковий чек, як if [ "$f" != "/media/*" ]; thenперед запуском find.
Pro резервне копіювання

12

є новий, стислий параметр systemdавтоматичного монтажу, який можна використовувати, за допомогою fstabякого ви можете використовувати всі стандартизовані параметри дозволу на монтаж, і це виглядає приблизно так:

  x-systemd.automount

приклад цього fstabрядка:

  /dev/sdd1   /mnt/hitachi-one     auto     noauto,x-systemd.automount     0 2

noautoваріант буде означати його не буде намагатися монтувати при завантаженні, так як зі старим програмним забезпеченням autofs.

після додавання нового x-systemd.automountрядка fstabвам потрібно запустити:

  sudo systemctl daemon-reload

а потім обидва або один із наступного:

  sudo systemctl restart remote-fs.target
  sudo systemctl restart local-fs.target

для отримання додаткової інформації про нього:

https://wiki.archlinux.org/index.php/Fstab#Automount_with_systemd


sudo systemctl restart local-fs.targetзробив для мене трюк
Філіп Гачуд

2

Я змінив сценарій з @MikeBlackwell на:

  • розпізнавати імена пристроїв, які охоплюють декілька символів, не тільки, /dev/sd[a-z]але й /dev/sd[a-z]*; часто трапляється з серверами, які мають більшу кількість шпинделів.
  • відстежувати список автоматичних накопичувачів на /var/log/usb-mount.track
  • записуйте дії до /var/log/messagesтегу usb-mount.sh
  • Приставка ім'я пристрою з міткою пристрою для монтажу точки не впадати в проблеми з дисками, які не було призначено мітками (порожній): /media/sdd2_usbtest,/media/sdd2_
  • включені сценарії обгортки, щоб розмістити файли належним чином та скасувати, якщо потрібно

Оскільки @MikeBlackwell вже зробив більшу частину важкого підйому, я вирішив не переписувати його; просто внесла необхідні зміни. Я визнав його роботу, бачачи його ім'я та URI оригінальної відповіді.

Знайдіть його на https://github.com/raamsri/automount-usb


2

Використовуючи підхід pmount , systemd та підхід Майка Блеквелла, ви можете спростити все:

/etc/systemd/system/usb-mount@.service

[Unit]
Description=Mount USB Drive on %i
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/pmount --umask 000 /dev/%i /media/%i
ExecStop=/usr/bin/pumount /dev/%i

/etc/udev/rules.d/99-usb-mount.rules

ACTION=="add",KERNEL=="sd[a-z][0-9]*",SUBSYSTEMS=="usb",RUN+="/bin/systemctl start usb-mount@%k.service"
ACTION=="remove",KERNEL=="sd[a-z][0-9]*",SUBSYSTEMS=="usb",RUN+="/bin/systemctl stop usb-mount@%k.service"

HTH і дякую Майку.


0

Я б пішов з відповіддю Уоррена Янга. Я вніс кілька змін

Я додав деякий захист простору, оскільки він давав помилки з eval середовища для диска.

Я додав розділ до chmod на USB-диску, щоб усі користувачі мали повний доступ до не ntfs або vfat дисків.

/usr/local/bin/usb-mount.sh

#!/bin/bash

# This script is called from our systemd unit file to mount or unmount
# a USB drive.

usage()
{
    echo "Usage: $0 {add|remove} device_name (e.g. sdb1)"
    exit 1
}

if [[ $# -ne 2 ]]; then
    usage
fi

ACTION="$1"
DEVBASE="$2"
DEVICE="/dev/${DEVBASE}"

# See if this drive is already mounted, and if so where
MOUNT_POINT=$(/bin/mount | /bin/grep ${DEVICE} | /usr/bin/awk '{ print $3 }')

do_mount()
{
    if [[ -n "${MOUNT_POINT}" ]]; then
        echo "Warning: ${DEVICE} is already mounted at ${MOUNT_POINT}"
        exit 1
    fi

    # Get info for this drive: $ID_FS_LABEL, $ID_FS_UUID, and $ID_FS_TYPE
    # added some sed's to avoid space issues
    eval $(/sbin/blkid -o udev ${DEVICE}|sed 's/=/="/'|sed 's/$/"/')

    # Figure out a mount point to use
    LABEL="${ID_FS_LABEL}"
    if [[ -z "${LABEL}" ]]; then
        LABEL="${DEVBASE}"
    elif /bin/grep -q " /media/${LABEL} " /etc/mtab; then
        # Already in use, make a unique one
        LABEL+="-${DEVBASE}"
    fi
    MOUNT_POINT="/media/${LABEL}"

    echo "Mount point: ${MOUNT_POINT}"

    /bin/mkdir -p "${MOUNT_POINT}"

    # Global mount options
    OPTS="rw,relatime"
    #added a chmod checker for file systems that don't 
    #understand allow all to read write
    CHMOD=no
    # File system type specific mount options
    if [[ ${ID_FS_TYPE} == "vfat" ]]; then
        OPTS+=",users,gid=100,umask=000,shortname=mixed,utf8=1,flush"
    #added options I wanted on ntfs
    elif [[ ${ID_FS_TYPE} == "ntfs" ]]; then
        OPTS+=",user,users,umask=000,allow_other"
    else
       CHMOD=yes
    fi

    if ! /bin/mount -o "${OPTS}" ${DEVICE} "${MOUNT_POINT}"; then
        echo "Error mounting ${DEVICE} (status = $?)"
        /bin/rmdir "${MOUNT_POINT}"
        exit 1
    fi


    echo "**** Mounted ${DEVICE} at ${MOUNT_POINT} ****"
    if [ "${CHMOD}" = "yes" ];then
        /usr/bin/find "${MOUNT_POINT}" -type f -exec chmod 0666 {} \;
        /usr/bin/find "${MOUNT_POINT}" -type d -exec chmod 0777 {} \;
    fi
}

do_unmount()
{
    if [[ -z ${MOUNT_POINT} ]]; then
        echo "Warning: ${DEVICE} is not mounted"
    else
        /bin/umount -l ${DEVICE}
        echo "**** Unmounted ${DEVICE}"
    fi

    # Delete all empty dirs in /media that aren't being used as mount
    # points. This is kind of overkill, but if the drive was unmounted
    # prior to removal we no longer know its mount point, and we don't
    # want to leave it orphaned...
    for f in /media/* ; do
        if [[ -n $(/usr/bin/find "$f" -maxdepth 0 -type d -empty) ]]; then
            if ! /bin/grep -q " $f " /etc/mtab; then
                echo "**** Removing mount point $f"
                /bin/rmdir "$f"
            fi
        fi
    done
}

case "${ACTION}" in
    add)
        do_mount
        ;;
    remove)
        do_unmount
        ;;
    *)
        usage
        ;;
 esac

Ви можете описати, чим відрізняється оригінальна відповідь від вашої у кількох словах, щоб зробити її кориснішою. PS: Воррена Янга відповіді не було; можливо ви мали на увазі відповідь Майка Блеквелла, яку редагували?
Амір
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.