1

The first attempt

In rules 51-android.rules, I added the execution of the program in case I connect my phone to a thin client:

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

My script /etc/udev/scripts/mtp.sh that performed device mounting when connected:

#!/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

P.S. I use the construction of recording logs as echo ..., since the logger from BusyBox does not write to the log on TS.

I used a logging script to see why my device was NOT mounted. Or rather, it was mounted and after some time fell off, while it was not visible in the system as a mounted device. Googling on the internet, I found information that systemd by default runs systemd-udevd.service with a separate "mount namespace".

The second attempt

I rewrote the rules in 51-android.rules as follows:

...
# 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"
...

and in /etc/systemd/system/[email protected] placed the following:

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

Accordingly, I rewrote the script /etc/udev/scripts/mtp.sh that would respond only to the add event:

#!/bin/sh

. /etc/thinstation.env
. $TS_GLOBAL

/bin/aft-mtp-mount /phone

It is logical to assume that during testing I unmounted the directory myself, without a script. As a result, the phone was not always connected the first time. It feels like he had some kind of timeout for the connection limit of a few minutes. In the end, I never figured out what the problem was.

The third attempt

My whole process of solving this problem is similar to something similar that is already available on stackoverflow. I tried to reproduce the solution from this post.

Corrected the 51-android.rules script in this way:

...
# 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"
...

and in /etc/systemd/system/[email protected] placed the following:

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

And the mount execution script /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

Now everything works as it was intended! But there is a nuance.

If the phone is initially attached to a thin client during its boot process, the system refuses to turn on. If the phone is disconnected during the boot process, then the thin client loads without problems and mounting also works after connection.

It seems that in the process of downloading a thin client, systemd-udevd tries to read the 51-android.rules rules and call the phone mount script, which creates a further problem with loading.

I tried to pull out the download data and this is what I saw:

Systemd services loading schedule

Kernel log

I don't understand, maybe I implemented everything incorrectly, or there are some pitfalls. It seems that I tried to try different options. It would be possible to try to mount the device via systemd-mount, but it does not support the MTP protocol to mount it. Or i can somehow implement it by other means? I have not found any additional information on this topic. I ask for help from those who know, since I have reached a dead end on the Arch forum. I have created themes on GitHub, but so far there is no progress either.

6
  • Perhaps you could just add an After=multi-user.target to the [Unit] part of the .service file to delay the mount.
    – meuh
    Commented Jan 29, 2023 at 17:16
  • @meuh No, it doesn't help. I find myself in this situation github.com/Thinstation/thinstation/issues/…. The system does not load further (or it loads, but I don't see anything except a black screen). It would be good to prohibit the implementation of the rules for android when downloading, but I do not know how to do this. After all, they are the trigger to start the systemd service and send the computer boot to hang. Commented Jan 30, 2023 at 5:09
  • You say systemd-mount does not support MTP, but what you might try is providing a mount helper, see man 8 mount "EXTERNAL HELPERS". If you specify to mount or systemd-mount the type -t mtp, and provide a program /sbin/mount.mtp, then mount will run that program to do the mount. This can be where you put /bin/aft-mtp-mount to do the actual work.
    – meuh
    Commented Jan 30, 2023 at 13:45
  • @meuh This is the first time I've heard that such a thing is possible. I have not stumbled upon this method for any example on the Internet. Why is there no such thing? It will be necessary to study. Commented Jan 30, 2023 at 17:57
  • 1
    I searched through the website for other answers using mount helpers and found 2 by me: this and also this which shows how the fuse type can take a subtype. I don't remember finding much documentation at the time.
    – meuh
    Commented Jan 31, 2023 at 8:21

1 Answer 1

1

I finally managed to solve the mounting problem! I spent three weeks searching for a more adequate solution.

To begin with, I applied these changes to the rules script for Android devices 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"

In this patch, I form a symbolic link to the attached device (I am forming a name from the properties of the device for a better understanding):

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"

I also specify the systemd tag (without it, the systemd service call does not work for me) and call the service based on my template (below) /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

In the system, my device looks like 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

In the handler script that is called systemd I create a directory (in my case it's Wileyfox_Swift_2_Plus_0318) and try to mount my device there. If successful, it is mounted, if not, then unmounting is triggered:

#! /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

Thus, I managed to mount the device and booting the operating system with the device connected to it will also work out without problems.

Demonstration of automatic mounting of an MTP device.

I made a repository if improvements are needed.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .