Ổ đĩa USB tự động với systemd


27

Chúng tôi đang cập nhật các máy chủ của chúng tôi từ một bản phân phối lỗi thời sang hệ thống dựa trên Debian Jessie hiện đại, bao gồm lightdm / xfce và dĩ nhiên là systemd (và udisks2). Một điểm gắn bó là tự động đếm các ổ USB. Chúng tôi đã từng thực hiện điều này với một số quy tắc udev. Các quy tắc cũ hầu như vẫn hoạt động - điểm gắn kết được tạo và ổ đĩa được gắn kết tốt, nhưng sau vài giây systemd đang làm gì đó phá vỡ giá trị gắn kết, do đó, các lần truy cập tiếp theo dẫn đến lỗi "Điểm cuối vận chuyển không được kết nối".

Tự gắn ổ đĩa thông qua dòng lệnh hoạt động tốt. Vì vậy, để cho một trình quản lý tập tin (thunar và thunar-volman, lần lượt sử dụng udisks2). Nhưng đó không phải là những lựa chọn khả thi - những hệ thống này hầu hết chạy không đầu, vì vậy thunar thường không chạy. Chúng ta cần có khả năng cắm vào ổ đĩa để sao lưu dự phòng dựa trên cron.

Tôi nghĩ rằng việc sửa đổi tập lệnh udev để tạo ra một công việc tách rời, đợi vài giây trước khi thực hiện việc gắn kết có thể sẽ tạo ra mánh khóe, nhưng systemd dường như đã hết cách để ngăn chặn điều này - bằng cách nào đó nó vẫn chờ công việc tách ra hoàn thành trước đó tiếp tục

Có lẽ có kịch bản udev tickle udisks2 bằng cách nào đó là cách tiếp cận đúng? Tôi thua cuộc, vì vậy mọi lời khuyên đều được đánh giá cao.


1
Chỉ liên quan một cách hữu hình, nhưng ... Bạn đang đặt xfce trên máy chủ?
Bắn Parthian

À, tôi đã sử dụng thuật ngữ "máy chủ" khá lỏng lẻo ... Tất cả sự tương tác của người dùng với hệ thống là thông qua một ứng dụng web, thường được truy cập qua trình duyệt qua mạng. Nhưng một số khách hàng thích giải pháp phi mạng, vì vậy chúng tôi chạy Chrome trên bảng điều khiển ở chế độ kiosk. (Điều này cũng thuận tiện để gỡ lỗi các vấn đề cấu hình mạng, bạn có thể cắm màn hình / chuột / bàn phím và truy cập các công cụ chẩn đoán cơ bản trong ứng dụng web mà không cần thông tin đăng nhập Linux). Có lẽ có một giải pháp trọng lượng nhẹ hơn lightdm / xfce, nhưng cách này đơn giản nhất để thiết lập ...
Mike Blackwell

Đối với bất kỳ ai muốn các quy tắc systemd-udevd trực tiếp chạy tập lệnh: Tôi đã có cái này; nó đã hoạt động được một lúc, nhưng tại một số thời điểm đã ngừng chạy tập lệnh nếu udevd đã được khởi động tự động. Dừng lại và khởi động lại từ dòng lệnh, và nó sẽ ổn thôi. Trên hết, nó không bao giờ hoạt động tốt với NTFS + FUSE vì udev đã phát hiện ra nó có quá trình con chạy dài (ntfs-3g) và giết chết nó sau 60 giây. Điểm mấu chốt: quy tắc udev trực tiếp chạy tập lệnh là một sự lãng phí thời gian. Đi với các quy tắc udev và một dịch vụ systemd thay vào đó, như đã lưu ý trong các câu trả lời. Sau đó, bạn không phải đối phó với các không gian tên (MountFlags = nô lệ).
Đánh dấu

Tôi gặp vấn đề tương tự về kịch bản bắt đầu bởi udev không thể thực hiện kết nối mạng. Giải pháp dưới đây về việc sử dụng systemd cũng hiệu quả với việc này - cảm ơn!
Quentin Stafford-Fraser

Câu trả lời:


28

Sau vài lần bắt đầu sai tôi đã tìm ra điều này. Điều quan trọng là thêm một dịch vụ đơn vị systemd giữa udev và tập lệnh mount.

(Đối với bản ghi, tôi không thể làm việc này bằng cách sử dụng udisks2 (thông qua một cái gì đó giống như udisksctl mount -b /dev/sdb1) được gọi trực tiếp từ quy tắc udev hoặc từ tệp đơn vị systemd. , kết quả là Error looking up object for device /dev/sdb1. Thật không may, vì udisks2 có thể xử lý tất cả sự lộn xộn của điểm gắn kết ...)

Việc nâng vật nặng được thực hiện bởi một tập lệnh shell, đảm nhiệm việc tạo và loại bỏ các điểm gắn kết, và gắn và ngắt kết nối các ổ đĩa.

/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

Kịch bản, lần lượt, được gọi bởi một tệp đơn vị systemd. Chúng tôi sử dụng cú pháp tên tệp "@" để chúng tôi có thể chuyển tên thiết bị làm đối số.

/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

Cuối cùng, một số quy tắc udev bắt đầu và dừng dịch vụ đơn vị systemd trên hotplug / rút phích cắm:

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

Điều này dường như để làm các mẹo! Một vài lệnh hữu ích để gỡ lỗi những thứ như thế này:

  • udevadm control -l debugbật ghi nhật ký chi tiết để /var/log/syslogbạn có thể thấy những gì đang xảy ra.
  • udevadm control --reload-rules sau khi bạn sửa đổi các tệp trong dir.d dir (có thể không cần thiết, nhưng không thể làm tổn thương ...).
  • systemctl daemon-reload sau khi bạn sửa đổi các tập tin đơn vị systemd.

4
Ồ Điều này thật tuyệt. Chúc tôi có thể cho nhiều upvote! Điều duy nhất tôi phải sửa đổi là trên hệ thống của tôi, blkiddường như không trích xuất được ID_FS_LABEL, vì vậy tôi chỉ sử dụng DEVBASEthay vì LABELxây dựng MOUNT_POINTthay thế.
Travis Griggs

Thiết lập này có thể được sửa đổi để hoạt động với các thiết bị ATA / SCSI không? Xem: serverfault.com/q/825779/297059
dùng339676

@Travis - Bạn có thể sử dụng udevadmthay vì blkid. Nó cung cấp nhiều chi tiết hơn cũng như thông tin bổ sung. (ví dụ udevadm info --query=property --name=sda1:)
dùng339676

điều này không hoạt động tốt khi khởi động, nếu thiết bị usb đã được kết nối. Có ý kiến ​​gì không?
Michal Artazov

Khi nullglobs không được đặt, khi ngắt kết nối, việc dọn dẹp có thể tạo ra lỗi như thế nào /usr/bin/find: '/media/*': No such file or directory. Dọn dẹp có thể sử dụng một kiểm tra bổ sung như if [ "$f" != "/media/*" ]; thentrước khi chạy find.
Sao lưu dự phòng

12

có một systemdtùy chọn tự động gắn kết mới, cô đọng , có thể được sử dụng fstabcho phép bạn sử dụng tất cả các tùy chọn cấp phép gắn kết được tiêu chuẩn hóa, và nó trông như thế này:

  x-systemd.automount

một ví dụ về nó trong một fstabdòng:

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

các noautotùy chọn sẽ có nghĩa là nó sẽ không cố gắng để được gắn kết lúc khởi động, như với phần mềm cũ autofs.

Sau khi thêm một x-systemd.automountdòng mới cho fstabbạn thì cần phải chạy:

  sudo systemctl daemon-reload

và sau đó cả hai, hoặc một, sau đây:

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

để biết thêm thông tin về nó:

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


sudo systemctl restart local-fs.targetđã lừa tôi
Philippe Gachoud

2

Tôi đã sửa đổi tập lệnh từ @MikeBlackwell thành:

  • nhận ra tên thiết bị trải rộng nhiều ký tự, không chỉ /dev/sd[a-z]nhưng /dev/sd[a-z]*; thường là trường hợp với các máy chủ có số lượng trục chính lớn hơn.
  • theo dõi danh sách các ổ đĩa tự động tại /var/log/usb-mount.track
  • đăng nhập các hành động /var/log/messagesvới thẻ usb-mount.sh
  • tên thiết bị tiền tố với nhãn thiết bị cho các điểm lắp để không chạy trong các vấn đề với ổ đĩa chưa được gán một nhãn (trống rỗng?): /media/sdd2_usbtest,/media/sdd2_
  • bao gồm các tập lệnh bao bọc để đặt các tập tin một cách thích hợp và hoàn tác nếu cần

Vì @MikeBlackwell đã thực hiện hầu hết các công việc nặng nhọc, tôi đã chọn không viết lại; chỉ cần thực hiện những thay đổi cần thiết. Tôi đã thừa nhận công việc của anh ấy khi thấy tên và URI của câu trả lời ban đầu.

Tìm nó tại https://github.com/raamsri/automount-usb


2

Sử dụng phương pháp của pmount , systemd và Mike Blackwell, bạn có thể đơn giản hóa toàn bộ:

/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 và cảm ơn bạn Mike.


0

Tôi sẽ đi với câu trả lời của Warren Young Tôi có một vài thay đổi tôi đã thực hiện

Tôi đã thêm một số bảo vệ không gian vì nó đã báo lỗi từ môi trường cho ổ đĩa.

Tôi đã thêm một phần để chmod một đĩa usb để tất cả người dùng có quyền truy cập đầy đủ vào các đĩa không ntfs hoặc 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

Bạn có thể muốn mô tả những gì khác nhau giữa câu trả lời ban đầu và của bạn trong một vài từ, để làm cho nó hữu ích hơn. Tái bút: không có câu trả lời của Warren Young; có lẽ bạn có nghĩa là câu trả lời của Mike Blackwell đã được chỉnh sửa?
Amir
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.