USB drive otomatis dengan systemd

27

Kami memperbarui server kami dari distro yang sangat ketinggalan zaman ke sistem berbasis Debian Jessie modern, termasuk lightdm / xfce, dan tentu saja systemd (dan udisks2). Satu hal yang menonjol adalah mengotomatiskan drive USB. Kami biasa melakukannya dengan beberapa aturan udev. Aturan lama hampir masih berfungsi - titik mount dapat dibuat dan drive dipasang dengan baik, tetapi setelah beberapa detik systemd melakukan sesuatu yang merusak mount, sehingga upaya akses selanjutnya menghasilkan kesalahan "Transport endpoint tidak terhubung".

Secara manual memasang drive melalui baris perintah berfungsi dengan baik. Begitu juga dengan membiarkan pengelola file (thunar dan thunar-volman, yang pada gilirannya menggunakan udisks2). Tapi itu bukan opsi yang layak - kebanyakan sistem ini berjalan tanpa kepala, jadi thunar biasanya tidak berjalan. Kita harus dapat menyambungkan disk drive untuk cadangan berbasis cron tanpa pengawasan.

Saya berpikir bahwa memodifikasi skrip udev untuk menelurkan pekerjaan terpisah yang menunggu beberapa detik sebelum melakukan mount mungkin melakukan trik, tetapi systemd tampaknya berusaha keras untuk mencegah ini - entah bagaimana masih menunggu pekerjaan yang terpisah untuk menyelesaikan sebelum melanjutkan.

Mungkin memiliki skrip udev menggelitik udisks2 entah bagaimana adalah pendekatan yang tepat? Saya bingung, jadi saran apa pun sangat dihargai.

Mike Blackwell
sumber
1
Hanya terkait secara tangensial, tapi ... Anda meletakkan xfce di server?
Parthian Shot
Ah, saya menggunakan istilah "server" agak longgar ... Semua interaksi pengguna dengan sistem adalah melalui aplikasi web, biasanya diakses melalui browser melalui jaringan. Tetapi beberapa pelanggan lebih suka solusi non-jaringan, jadi kami menjalankan Chrome di konsol dalam semacam mode kios. (Ini juga berguna untuk debugging masalah konfigurasi jaringan, Anda dapat mencolokkan monitor / mouse / keyboard dan mengakses alat diagnostik dasar di aplikasi web tanpa memerlukan kredensial login Linux). Mungkin ada solusi berat yang lebih ringan daripada lightdm / xfce, tapi ini paling sederhana untuk diatur ...
Mike Blackwell
Bagi siapa pun yang menginginkan aturan systemd-udevd langsung menjalankan skrip: Saya punya ini; itu berfungsi untuk sementara waktu, tetapi pada beberapa titik berhenti menjalankan skrip jika udevd telah dimulai secara otomatis. Berhenti dan mulai lagi dari baris perintah, dan itu akan baik-baik saja. Selain itu, tidak pernah bekerja dengan baik dengan NTFS + FUSE karena udev mendeteksi itu memiliki proses anak yang berjalan lama (ntfs-3g) dan membunuhnya setelah 60-an. Intinya: aturan udev langsung menjalankan skrip adalah buang-buang waktu. Pergi dengan aturan udev dan layanan systemd sebagai gantinya, seperti yang tercantum dalam jawaban. Maka Anda tidak harus berurusan dengan ruang nama (MountFlags = slave) juga.
Markus
Saya memiliki masalah yang sama dengan skrip yang dimulai oleh udev karena tidak dapat membuat koneksi jaringan. Solusi di bawah ini menggunakan systemd bekerja untuk ini juga - terima kasih!
Quentin Stafford-Fraser

Jawaban:

28

Setelah beberapa kesalahan dimulai saya menemukan ini. Kuncinya adalah menambahkan layanan unit systemd antara udev dan skrip pemasangan.

(Sebagai catatan, saya tidak bisa mendapatkan ini bekerja menggunakan udisks2 (melalui sesuatu seperti udisksctl mount -b /dev/sdb1) dipanggil baik langsung dari aturan udev atau dari file unit systemd. Tampaknya ada kondisi lomba dan node perangkat tidak cukup siap , mengakibatkan Error looking up object for device /dev/sdb1. Sayangnya, karena udisks2 dapat mengatasi semua kekacauan titik mount ...)

Pengangkatan berat dilakukan oleh skrip shell, yang menangani pembuatan dan penghapusan titik pemasangan, dan pemasangan dan pelepasan drive.

/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

Script, pada gilirannya, dipanggil oleh file unit systemd. Kami menggunakan sintaks nama file "@" sehingga kami dapat meneruskan nama perangkat sebagai argumen.

/etc/systemd/system/[email protected]

[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

Akhirnya, beberapa aturan udev memulai dan menghentikan layanan unit systemd pada hotplug / cabut:

/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"

Ini sepertinya berhasil! Beberapa perintah yang berguna untuk men-debug hal-hal seperti ini:

  • udevadm control -l debugmengaktifkan log masuk /var/log/syslogsehingga Anda dapat melihat apa yang terjadi.
  • udevadm control --reload-rules setelah Anda memodifikasi file dalam aturan.d dir (mungkin tidak perlu, tetapi tidak ada salahnya ...).
  • systemctl daemon-reload setelah Anda memodifikasi file unit systemd.
Mike Blackwell
sumber
4
Wow. Ini luar biasa. Seandainya saya bisa memberikan banyak upvotes! Satu-satunya hal yang harus saya modifikasi adalah bahwa pada sistem saya, blkidsepertinya tidak mengekstrak ID_FS_LABEL, jadi saya hanya menggunakan DEVBASEdaripada LABELuntuk membangunnya MOUNT_POINTsaja.
Travis Griggs
Bisakah pengaturan ini diubah agar berfungsi dengan perangkat ATA / SCSI? Lihat: serverfault.com/q/825779/297059
user339676
@ Travis - Anda bisa menggunakannya udevadmsebagai ganti blkid. Ini memberikan lebih banyak detail serta informasi tambahan. (mis. udevadm info --query=property --name=sda1)
user339676
ini tidak berfungsi dengan baik pada saat boot, jika perangkat usb sudah terhubung. Ada ide?
Michal Artazov
Ketika nullglobs tidak disetel, pada unmount, pembersihan dapat menghasilkan galat seperti /usr/bin/find: '/media/*': No such file or directory. Pembersihan dapat menggunakan pemeriksaan tambahan seperti if [ "$f" != "/media/*" ]; thensebelum menjalankan find.
Pro Backup
12

ada systemdopsi pemasangan otomatis yang ringkas dan baru yang dapat digunakan fstabyang memungkinkan Anda untuk menggunakan semua opsi izin pemasangan standar, dan ini terlihat seperti ini:

  x-systemd.automount

contoh dalam satu fstabbaris:

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

yang noautopilihan akan berarti itu tidak akan berusaha untuk dipasang di boot, seperti dengan perangkat lunak yang lebih tua autofs.

setelah menambahkan x-systemd.automountbaris baru untuk fstabAnda maka perlu menjalankan:

  sudo systemctl daemon-reload

dan kemudian keduanya, atau satu, dari yang berikut:

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

untuk informasi lebih lanjut tentang itu:

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

infinite-dan sebagainya
sumber
sudo systemctl restart local-fs.targetmelakukan trik untuk saya
Philippe Gachoud
2

Saya telah memodifikasi skrip dari @MikeBlackwell ke:

  • kenali nama-nama perangkat yang menjangkau beberapa karakter, tidak hanya /dev/sd[a-z]tetapi /dev/sd[a-z]*; sering terjadi dengan server yang memiliki jumlah spindel yang lebih besar.
  • lacak daftar drive yang diotomatisasi di /var/log/usb-mount.track
  • catat tindakan /var/log/messagesdengan tag usb-mount.sh
  • awalan nama perangkat dengan label perangkat untuk mount point untuk tidak menjalankan ke masalah dengan drive yang belum ditugaskan label (kosong?): /media/sdd2_usbtest,/media/sdd2_
  • termasuk skrip pembungkus untuk menempatkan file dengan tepat dan membatalkan jika diperlukan

Karena @MikeBlackwell telah melakukan sebagian besar pengangkatan berat, saya memilih untuk tidak menulis ulang; baru saja membuat perubahan yang diperlukan. Saya telah mengakui pekerjaannya melihat namanya dan URI dari jawaban aslinya.

Temukan di https://github.com/raamsri/automount-usb

enam k
sumber
2

Menggunakan pendekatan pmount , systemd, dan Mike Blackwell, Anda dapat menyederhanakan semuanya:

/etc/systemd/system/[email protected]

[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 dan terima kasih Mike.

Eric V.
sumber
0

Saya akan menjawab dengan Warren Young. Saya memiliki beberapa perubahan yang saya buat

Saya menambahkan beberapa perlindungan ruang karena memberikan kesalahan dari eval lingkungan untuk drive.

Saya menambahkan bagian ke chmod disk usb sehingga semua pengguna memiliki akses penuh ke disk non ntfs atau 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
penguinjeff
sumber
Anda mungkin ingin menggambarkan apa yang berbeda antara jawaban asli dan jawaban Anda dalam beberapa kata, agar lebih bermanfaat. PS: tidak ada jawaban oleh Warren Young; mungkin maksud Anda jawaban Mike Blackwell yang diedit?
Amir