systemd를 사용하여 MTP 장치 마운트

systemd를 사용하여 MTP 장치 마운트

첫 시도

규칙에51-android.rules, 휴대폰을 씬 클라이언트에 연결할 경우를 대비해 프로그램 실행을 추가했습니다.

...
# Skip other vendor tests
LABEL="android_usb_rule_match"
RUN+="/etc/udev/scripts/mtp.sh"
...

내 스크립트는 /etc/udev/scripts/mtp.sh연결되면 장치 설치를 수행합니다.

#!/bin/sh

. /etc/thinstation.env
. $TS_GLOBAL

if [[ $ACTION == "add" ]]; then
    echo "********************* START MOUNT *******************" | systemd-cat -p info -t "mtp"
    /bin/aft-mtp-mount /phone
    grep -oe "/phone" /proc/mounts | systemd-cat -p err -t "mtp"
    ls "/phone/Внутренний общий накопитель" | systemd-cat -p err -t "mtp"
    echo "********************** END MOUNT ********************" | systemd-cat -p info -t "mtp"
elif [[ $ACTION == "remove" ]]; then
    echo "******************** START UNMOUNT ******************" | systemd-cat -p info -t "mtp"
    umount /phone
    echo "********************* END UNMOUNT *******************" | systemd-cat -p info -t "mtp"
fi

PS BusyBox는 TS에 로그를 쓰지 않기 echo ...때문에 로깅 구조를 사용하고 있습니다 .logger

내 장치가 마운트되지 않는 이유를 확인하기 위해 로깅 스크립트를 사용했습니다. 혹은 오히려 설치했다가 시간이 지나서 떨어져 나갔지만 시스템에는 설치된 장치로 보이지 않았습니다. 인터넷에서 구글링해서 찾은정보systemd는 기본적으로 별도의 "마운트 네임스페이스"를 사용하여 systemd-udevd.service를 실행합니다.

두 번째 시도

규칙을 다시 썼어요51-android.rules다음과 같이:

...
# Skip other vendor tests
LABEL="android_usb_rule_match"
ACTION=="add", PROGRAM="/bin/aft-mtp-mount -p [email protected] $env{ID_MODEL}_$env{ID_VENDOR}", ENV{SYSTEMD_WANTS}+="%c"
...

그리고 다음을 입력하세요:/etc/systemd/system/[email protected]

[Service]
Type=oneshot
ExecStart=/etc/udev/scripts/mtp.sh

/etc/udev/scripts/mtp.sh그래서 이벤트에만 응답하도록 스크립트를 다시 작성했습니다 add.

#!/bin/sh

. /etc/thinstation.env
. $TS_GLOBAL

/bin/aft-mtp-mount /phone

테스트하는 동안 스크립트를 사용하지 않고 직접 디렉토리를 마운트 해제했다고 가정하는 것이 합리적입니다. 결과적으로 전화는 항상 처음 연결에 실패합니다. 몇 분의 연결 제한에 일종의 시간 초과가 있는 것처럼 느껴집니다. 결국 나는 문제가 무엇인지 파악하지 못했습니다.

세 번째 시도

이 문제를 해결하기 위한 전체 프로세스는 다음과 비슷했습니다.stackoverflow에서 이미 사용 가능. 이 게시물에서 솔루션을 재현하려고 합니다.

수정됨51-android.rules스크립트는 다음과 같습니다.

...
# Skip other vendor tests
LABEL="android_usb_rule_match"
ACTION=="add", RUN+="/bin/systemctl start mtp@$env{ID_VENDOR}_$env{ID_MODEL}_$env{ID_REVISION}.service"
ACTION=="remove", RUN+="/bin/systemctl stop mtp@$env{ID_VENDOR}_$env{ID_MODEL}_$env{ID_REVISION}.service"
...

그리고 다음을 입력하세요:/etc/systemd/system/[email protected]

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/etc/udev/scripts/mtp.sh add %I
ExecStop=/etc/udev/scripts/mtp.sh remove %I

그리고 실행 스크립트를 마운트합니다 /etc/udev/scripts/mtp.sh.

#! /bin/sh

. /etc/thinstation.env
. $TS_GLOBAL

ACTION=$1
DEVICE_NAME=$2
MOUNT=/bin/aft-mtp-mount

CURRENT_DEVICE_MOUNT_PATH=$BASE_MOUNT_PATH/$USB_MOUNT_DIR/$DEVICE_NAME

_logger() {
    echo "$2" | systemd-cat -p $1 -t "mtp"
}

_mounted() {
    if [ -n "$(grep -oe "$1" /proc/mounts)" ]; then
        return 0
    else
        return 1
    fi
}

_mount() {
    _logger info "mount $DEVICE_NAME"
    if [ -d $CURRENT_DEVICE_MOUNT_PATH ] && _mounted $CURRENT_DEVICE_MOUNT_PATH; then
        _logger warning "$DEVICE_NAME already mounted"
        exit 1
    fi
    if [ ! -d $CURRENT_DEVICE_MOUNT_PATH ]; then
        mkdir $CURRENT_DEVICE_MOUNT_PATH
        if is_enabled "$USB_STORAGE_SYNC" && [ ! -n "$(echo $USB_MOUNT_OPTIONS | grep -e sync)" ]; then
            USB_MOUNT_OPTIONS=$USB_MOUNT_OPTIONS,sync
        fi
        $MOUNT -o $USB_MOUNT_OPTIONS $CURRENT_DEVICE_MOUNT_PATH
        if _mounted $CURRENT_DEVICE_MOUNT_PATH && [ "$(ls -A $CURRENT_DEVICE_MOUNT_PATH)" ]; then
            _logger info "mounted $DEVICE_NAME in $CURRENT_DEVICE_MOUNT_PATH"
        else
            _logger warning "$DEVICE_NAME failed to mount"
            _umount
            exit 2
        fi
    else
        _logger warning "$CURRENT_DEVICE_MOUNT_PATH already exists"
        exit 3
    fi
}

_umount() {
    _logger info "unmount $DEVICE_NAME"
    if [[ -d $CURRENT_DEVICE_MOUNT_PATH ]]; then
        while _mounted $CURRENT_DEVICE_MOUNT_PATH; do
            umount $CURRENT_DEVICE_MOUNT_PATH
        done
        _logger info "unmounted $DEVICE_NAME in $CURRENT_DEVICE_MOUNT_PATH"
        rm -r $CURRENT_DEVICE_MOUNT_PATH
    else
        _logger warning "$DEVICE_NAME was not mounted"
    fi
}

if [ $ACTION == "add" ]; then
    _mount
elif [ $ACTION == "remove" ]; then
    _umount
fi

exit 0

이제 모든 것이 예상대로 작동합니다! 그러나 뉘앙스가 있습니다.

시작하는 동안 전화기가 처음에 씬 클라이언트에 연결되면 시스템이 열리지 않습니다. 시작하는 동안 전화 연결이 끊어지면 씬 클라이언트를 로드하는 데 문제가 없으며 연결되면 설치가 제대로 작동합니다.

씬 클라이언트를 다운로드하는 동안 systemd-udevd읽기를 시도한 것 같습니다.51-android.rules규칙을 적용하고 모바일 설치 스크립트를 호출하므로 추가 로딩 문제가 발생합니다.

다운로드 데이터를 추출해 보았더니 다음과 같습니다.

시스템화된 서비스 로딩 일정

커널 로그

이해가 안 돼요. 구현이 모두 잘못되었거나 문제가 있을 수도 있습니다. 다양한 옵션을 시도하고 있는 것 같습니다. 장치 마운트를 시도할 수 있지만 systemd-mountMTP 프로토콜을 통한 마운트는 지원되지 않습니다. 아니면 다른 방법으로 달성할 수 있나요? 이 주제에 대한 다른 정보를 찾지 못했습니다. 막다른 골목에 이르렀기 때문에 아시는 분에게 도움을 요청합니다.아치법정. 테마를 만들어봤습니다GitHub, 그러나 현재까지 아무런 진전이 없습니다.

답변1

드디어 설치 문제를 해결했습니다! 저는 더 적합한 솔루션을 찾는데 3주를 보냈습니다.

먼저 Android 기기의 규칙 스크립트에 이러한 변경 사항을 적용합니다.51-android.rules /etc/udev/rules.d/51-android.rules:

diff --git a/51-android.rules b/51-android.rules
index d75ddb3..65f235c 100644
--- a/51-android.rules
+++ b/51-android.rules
@@ -9,7 +9,7 @@
 # https://github.com/M0Rf30/android-udev-rules
 
 # Skip testing for android devices if device is not add, or usb
-ACTION!="add", ACTION!="bind", GOTO="android_usb_rules_end"
+ENV{DEVTYPE}!="usb_device", GOTO="android_usb_rules_end"
 SUBSYSTEM!="usb", GOTO="android_usb_rules_end"
 
 # Skip testing for unexpected devices like hubs, controllers or printers
@@ -820,13 +820,16 @@ GOTO="android_usb_rule_match"
 LABEL="not_ZTE"
 
 # ZUK
-ATTR{idVendor}=="2b4c", ENV{adb_user}="yes"
+ATTR{idVendor}=="2b4c", ENV{adb_user}="yes", GOTO="android_usb_rule_match"
 
 # Verifone
-ATTR{idVendor}=="11ca", ENV{adb_user}="yes"
+ATTR{idVendor}=="11ca", ENV{adb_user}="yes", GOTO="android_usb_rule_match"
+
+GOTO="android_usb_rules_end"
 
 # Skip other vendor tests
 LABEL="android_usb_rule_match"
+TAG+="systemd", SYMLINK+="$env{ID_VENDOR}_$env{ID_MODEL}_$env{ID_REVISION}", ENV{SYSTEMD_WANTS}+="mtp@$env{ID_VENDOR}_$env{ID_MODEL}_$env{ID_REVISION}.service"
 
 # Symlink shortcuts to reduce code in tests above
 ENV{adb_adbfast}=="yes", ENV{adb_adb}="yes", ENV{adb_fast}="yes"

이 패치에서는 연결된 장치에 대한 심볼릭 링크를 형성했습니다(이해를 돕기 위해 기기의 속성을 바탕으로 이름을 짓고 있습니다.):

TAG+="systemd", SYMLINK+="$env{ID_VENDOR}_$env{ID_MODEL}_$env{ID_REVISION}", ENV{SYSTEMD_WANTS}+="mtp@$env{ID_VENDOR}_$env{ID_MODEL}_$env{ID_REVISION}.service"

나는 또한 systemd라벨(그것이 없으면 시스템 서비스 호출이 작동하지 않습니다.) 내 템플릿(아래)을 기반으로 서비스를 호출합니다./etc/systemd/system/[email protected]

[Unit]
Description=Mounting MTP devices
BindsTo=dev-%i.device
After=dev-%i.device

[Service]
Type=oneshot
RemainAfterExit=true
TimeoutStartSec=30
ExecStart=/etc/udev/scripts/mtp.sh add %I
ExecStop=/etc/udev/scripts/mtp.sh remove %I

[Install]
WantedBy=dev-%i.device

시스템에서 내 장치는 다음과 같습니다 dev-NAME.device.

~ # systemctl --all --full -t device | grep Swift
dev-android.device                                               loaded active plugged Swift_2_Plus                                                                         
dev-android4.device                                              loaded active plugged Swift_2_Plus                                                                         
dev-bus-usb-001-007.device                                       loaded active plugged Swift_2_Plus                                                                         
dev-Wileyfox_Swift_2_Plus_0318.device                            loaded active plugged Swift_2_Plus                                                                         
sys-devices-pci0000:00-0000:00:15.0-usb1-1\x2d4.device           loaded active plugged Swift_2_Plus

호출된 핸들러 스크립트에서 systemd디렉토리(제 경우에는 )를 생성 Wileyfox_Swift_2_Plus_0318하고 거기에 장치를 마운트하려고 합니다. 성공하면 마운트하고, 실패하면 마운트 해제를 트리거합니다.

#! /bin/sh

. /etc/thinstation.env
. $TS_GLOBAL

ACTION=$1
DEVICE_NAME=$2
MOUNT=/bin/aft-mtp-mount

CURRENT_DEVICE_MOUNT_PATH=$BASE_MOUNT_PATH/$USB_MOUNT_DIR/$DEVICE_NAME

_logger() {
    echo "$2" | systemd-cat -p $1 -t "mtp"
}

_mounted() {
    if [ -n "$(grep -oe "$1" /proc/mounts)" ]; then
        return 0
    else
        return 1
    fi
}

_mount() {
    _logger info "mount $DEVICE_NAME"
    if [ -d $CURRENT_DEVICE_MOUNT_PATH ] && _mounted $CURRENT_DEVICE_MOUNT_PATH; then
        _logger warning "$DEVICE_NAME already mounted"
        exit 1
    fi
    if [ ! -d $CURRENT_DEVICE_MOUNT_PATH ]; then
        mkdir $CURRENT_DEVICE_MOUNT_PATH
        if is_enabled "$USB_STORAGE_SYNC" && [ ! -n "$(echo $USB_MOUNT_OPTIONS | grep -e sync)" ]; then
            USB_MOUNT_OPTIONS=$USB_MOUNT_OPTIONS,sync
        fi
        $MOUNT -o $USB_MOUNT_OPTIONS $CURRENT_DEVICE_MOUNT_PATH
        if _mounted $CURRENT_DEVICE_MOUNT_PATH && [ "$(ls -A $CURRENT_DEVICE_MOUNT_PATH)" ]; then
            _logger info "mounted $DEVICE_NAME in $CURRENT_DEVICE_MOUNT_PATH"
        else
            _logger warning "$DEVICE_NAME failed to mount"
            _umount
            exit 2
        fi
    else
        _logger warning "$CURRENT_DEVICE_MOUNT_PATH already exists"
        exit 3
    fi
}

_umount() {
    _logger info "unmount $DEVICE_NAME"
    if [[ -d $CURRENT_DEVICE_MOUNT_PATH ]]; then
        while _mounted $CURRENT_DEVICE_MOUNT_PATH; do
            umount $CURRENT_DEVICE_MOUNT_PATH
        done
        _logger info "unmounted $DEVICE_NAME in $CURRENT_DEVICE_MOUNT_PATH"
        rm -r $CURRENT_DEVICE_MOUNT_PATH
    else
        _logger warning "$DEVICE_NAME was not mounted"
    fi
}

if [ $ACTION == "add" ]; then
    _mount
elif [ $ACTION == "remove" ]; then
    _umount
fi

exit 0

그래서 장치를 성공적으로 설치하고 문제 없이 장치에 연결된 OS를 부팅했습니다.

MTP 장비 자동 설치 시연.

개선이 필요한 경우 저장소를 만들었습니다..

관련 정보