24 Mar

USB Automount Script for Raspberry Pi

RaspberryUSBWhen working with a raspberry pi, you might need some USB storage to carry your data. I for example use a raspberry as a quite powerful seafile cloud server, which will probably be presented in a later post. As I was working with usb storage on raspberry pis, I became pretty bored on mounting devices manually and unmount it later aswell. The installable ‘automount’ program didn’t suit my flavor by adding unlabeled usb* folders to /media/*. I want to see the correct device name like on a regular desktop computer. So, I wrote a handy USB Automount Script for Raspberry Pis with the according udev rule to execute it when required.

The main script can be downloaded here:

[Download not found]

Here is, how you implement it:

The udev rules

sudo nano /etc/udev/rules.d/00-mount_manager

EDIT: For raspberry pi 3 or older pi’s with possible newer OS versions use this instead:

sudo nano /etc/udev/rules.d/00-mount_manager.rules

→ This opens the text editor for a new file ’00-mount_manager’.

Write or copy the following

ACTION=="add",SUBSYSTEM=="block",KERNEL=="sd*[!0-9]",RUN+="/root/mount_manager/mount_manager add"
ACTION=="remove",SUBSYSTEM=="block",KERNEL=="sd*[!0-9]",RUN+="/root/mount_manager/mount_manager remove"

→ The first line calls our script when a new usb device is plugged in. The second line is called when a device is unplugged. The rules are not active yet.

The main script

sudo mkdir /root/mount_manager
sudo nano /root/mount_manager/mount_manager

→ With this two lines we chreate a directory and the main script file. After this copy the following code to the new file.

#!/bin/bash

APP_DIR=/root/mount_manager
LOG_DIR=$APP_DIR
LOG_FILE="${LOG_DIR}/mount_manager.log"
MOUNT_D=/media
MOUNT_DIR=$MOUNT_D/

log_private=true
log_syslog=true

user=root
group=root

arr_media_mounted=()
arr_media_unmounted=()
arr_plugged_in_path=()
arr_plugged_in_label=()
arr_plugged_in_type=()

# function for mounting different file systems
function mounter {
    # $1 device path /dev/sd*
    # $2 name or label of device
    # $3 format or type

    if [ "vfat" == "$3" ]; then
        mount -t vfat -o utf8,uid=${user},gid=${group} $2 $MOUNT_DIR$1 >> $LOG_FILE 2>&1
    fi
        if [ "ntfs" == "$3" ]; then
        nohup mount -t ntfs -o rw $2 $MOUNT_DIR$1 >> $LOG_FILE 2>&1 &
    fi
        if [ "hfsplus" == "$3" ]; then
        mount -t hfsplus -o utf8,uid=${user},gid=${group} $2 $MOUNT_DIR$1 >> $LOG_FILE 2>&1 #not tested
    fi
        if [ "exfat" == "$3" ]; then
        mount -t exfat -o utf8,uid=${user},gid=${group} $2 $MOUNT_DIR$1 >> $LOG_FILE 2>&1 #not tested
    fi
        if [ "ext4" == "$3" ]; then
        mount -t ext4 -o defaults $2 $MOUNT_DIR$1 >> $LOG_FILE 2>&1
    fi
}

# functions for loggin output
function log {
    if [ "$log_private" = true ] ; then
        echo "$(date +"%Y.%m.%d %H:%M:%S") $1" >> $LOG_FILE
    fi
}
function log_info {
    log "INFO: $1"
        if [ "$log_syslog" = true ] ; then
        logger $1 -p info -t ${0#./}
    fi
}
function log_warn {
    log "WARNING: $1"
        if [ "$log_syslog" = true ] ; then
                logger $1 -p warning -t ${0#./}
    fi
}
function log_err {
        log "ERROR: $1"
        if [ "$log_syslog" = true ] ; then
                logger $1 -p error -t ${0#./}
    fi
}

# backup log
if [ "$(wc -l < $LOG_FILE)" -gt 200 ]; then
    mv $LOG_FILE ${LOG_FILE}.1
fi


######################################################
#
#       start main routine

log_info "========== mount_manager called by udev $1 =========="


# get directories in mounting directory $MOUNT_DIR
for dir in $(ls -1 "${MOUNT_D}"); do
    if mount | grep $dir > /dev/null; then
        arr_media_mounted+=(${dir#$MOUNT_DIR})
    else
        arr_media_unmounted+=(${dir#$MOUNT_DIR})
    fi
done


# get plugged in devices
regex='(/dev/sd[a-z][0-9]): (LABEL=\"([A-Za-z0-9_\-]+)\")?.*UUID=\"([a-zA-Z0-9\-]+)\".*TYPE=\"([a-zA-Z0-9]+)\"'
while IFS= read -r line; do
        if [[ $line =~ $regex ]];then
                name="${BASH_REMATCH[1]}"
                label="${BASH_REMATCH[3]}"
                uuid="${BASH_REMATCH[4]}"
                type="${BASH_REMATCH[5]}"
                if [ -z "${label}" ];then
                        label=${uuid}
                fi
                arr_plugged_in_path+=($name)
                arr_plugged_in_label+=($label)
                arr_plugged_in_type+=($type)
        fi
done < <(blkid)


# check directories in /media yet not plugged in devices
for mounted_item in ${arr_media_mounted[*]}
do
    if ! [[ " ${arr_plugged_in_label[*]} " == *" ${mounted_item} "* ]]; then
        log_info "Mounted yet unplugged directory '${mounted_item}' found."
        umount $MOUNT_DIR$mounted_item >> $LOG_FILE 2>&1
        if ! [ "$(ls -A $MOUNT_DIR$mounted_item)" ]; then
            log_info " - Directory now unmounted and empty and will be deleted."
            rm -rf $MOUNT_DIR$mounted_item >> $LOG_FILE 2>&1
        else
            log_err " - Directory not empty. This should not happen after unmount. Please check '${$MOUNT_DIR$mounted_item}'"
        fi
    fi
done
for unmounted_item in ${arr_media_unmounted[*]}
do
        if ! [[ " ${arr_plugged_in_label[*]} " == *" ${unmounted_item} "* ]]; then
                log_info "Unmounted and unplugged directory '${unmounted_item}' found."
        if ! [ "$(ls -A $MOUNT_DIR$unmounted_item)" ]; then
            log_info " - Directory is empty and will be deleted."
            rm -rf $MOUNT_DIR$unmounted_item >> $LOG_FILE 2>&1
        else
            log_warn " - Directory not empty. No further action."
        fi
        fi
done

# check plugged in devices
i=0
for plugged_in_item in ${arr_plugged_in_label[*]}
do
    log_info "Plugged in device '${plugged_in_item}' will be checked."
    if [[ " ${arr_media_mounted[*]} " == *" ${plugged_in_item} "* ]]; then
        log_info " - Mounted device '${plugged_in_item}' found. Great."
    else
        if [[ " ${arr_media_unmounted[*]} " == *" ${plugged_in_item} "* ]]; then
            log_info " - Unmounted yet plugged in device '${plugged_in_item}' found. It will be mounted."
            else
            log_info " - Newly plugged in device '${plugged_in_item}' found. Directory will be created and device will be mounted."
            mkdir $MOUNT_DIR$plugged_in_item >> $LOG_FILE 2>&1
        fi
        mounter $plugged_in_item ${arr_plugged_in_path[i]} ${arr_plugged_in_type[i]}
    fi
    ((i++))
done

Now close the editor with and make the script executable:

Ctrl + C and Y
sudo chmod o+x /root/mount_manager/mount_manager
sudo udevadm control --reload-rules

→ This line reloads all udev rules. So your script is active now.

Whenever you plugin a USB drive (formatting: fat, ntfs, hfsplus, exfat, ext4) it will be mounted under /media/<DEVICE_NAME>.

Config/Notes

  • By default the script creates a log file under ‘/root/mount_manager/mount_manager.log’ and logs its output to syslog.
    If you want to disable logging set if false with lines 8-9:

    log_private=false
    log_syslog=false
  • Some file formates require to be mounted for a specific user/group. By default the script is set to root:root in line 11-12. Change those to ‘pi’ (or anything else) if your normal pi user shall be able to access the device.
  • The bash script was testes with my raspberry pi B and my raspberry pi 2B with raspbian jessie or wheezy installed.

Update:

  • Currently the script does not support multiple usb drives with the same label. I will add this function soon.

If you like the script, please leave a comment.

 

35 thoughts on “USB Automount Script for Raspberry Pi

  1. Well, actually ‘/root/mount_manager/mount_manager.log’ doesn’t get created.
    Syslog displays what you (or at least I) would expect when you insert a USB HDD.

    [ 469.591649] usb 1-1.3: new high-speed USB device number 5 using dwc_otg
    [ 469.740578] usb 1-1.3: New USB device found, idVendor=067b, idProduct=2773
    [ 469.740650] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
    [ 469.740676] usb 1-1.3: Product: ATAPI-6 Bridge Controller
    [ 469.740696] usb 1-1.3: Manufacturer: Prolific Technology Inc.
    [ 469.740715] usb 1-1.3: SerialNumber: 0123456789000000005
    [ 469.762625] usb-storage 1-1.3:1.0: USB Mass Storage device detected
    [ 469.766933] scsi host1: usb-storage 1-1.3:1.0
    [ 470.763114] scsi 1:0:0:0: Direct-Access WDC WD50 00AAKX-001CA0 15.0 PQ: 0 ANSI: 0
    [ 470.769096] sd 1:0:0:0: [sda] 976773168 512-byte logical blocks: (500 GB/466 GiB)
    [ 470.770622] sd 1:0:0:0: [sda] Write Protect is off
    [ 470.770664] sd 1:0:0:0: [sda] Mode Sense: 03 00 00 00
    [ 470.771477] sd 1:0:0:0: [sda] No Caching mode page found
    [ 470.771514] sd 1:0:0:0: [sda] Assuming drive cache: write through
    [ 470.782237] sd 1:0:0:0: Attached scsi generic sg0 type 0
    [ 470.802901] sda: sda1
    [ 470.807617] sd 1:0:0:0: [sda] Attached SCSI disk
    [ 471.442214] EXT4-fs (sda1): mounted filesystem with ordered data mode. Opts: (null)

    Presently I have added a line in fstab to have the HDD mounted on start.
    But your method appealed greatly to me, and it would be so nice to get it to work! :)

    /Petter

    • Hi Petter,

      (sorry for the late reply, I was on vacation)

      Do you have logging active in my script by lines
      log_private=true
      log_syslog=true

      ?

      If yes and you have no logging file, then we can say (with certainty) that the udev rule didn’t apply or some user rights are wrong, so the script does not execute.

      Do you run as root user and did you change access rights?

      Try
      sudo chmod o+x /root/mount_manager/mount_manager
      and maybe
      sudo chmod o+x /etc/udev/rules.d/00-mount_manager

  2. The regex in line 89 has an error: for my Raspberry Pi; the word ‘LABEL’ has to be replaced by ‘UUID’. The same is true for Mageia 5.

  3. Great idea but the script does not work for me either… :-( Log is enabled but there is no log file. The device (NTFS HDD) is mounted on /media/pi/NAME by udev (I think) but not by your script. Maybe there is a udev rule that is preferred over the 00-mount_manager rule??

    • Hi Thorsten,
      if you say the device is mounted but not by the script, how do you mean this? Of course if other programs do some mounting, this might interfere.
      For starters: What other udev rules do you see on your pi?

  4. Hello bastian, thanks for the code. I am trying it on a ntsf usb hdd (“batarang”), but get stuck at some point:
    1) file in rules.d is called okay, script is called, log files says:

    2016.08.16 21:54:08 INFO: ========== mount_manager called by udev add ==========
    2016.08.16 21:54:08 INFO: Plugged in device ‘Batarang’ will be checked.
    2016.08.16 21:54:08 INFO: – Newly plugged in device ‘Batarang’ found. Directory will be created and device will be mounted.

    2) hdd is mounted to /media/pi/Batarang, but is not accessible, since “the socket is not connected”
    “ls” gives:

    d????????? ? ? ? ? ? Batarang

    Do you have any idea what this means?

    Best,
    Max

      • Hi bastian,

        I just checked: It’s working fine with FAT drives, but apparently cannot handle NTFS devices so well. I tried different combinations, e.g. with the target directory of the mount command already existing, using 777 access rights, but nothing changed. As soon as the script tries to mount it, it’s not accessible anymore.

        I can try to run the script manually, which works fine using “sudo ./mount_manager add”, however “./mount_manager add” is not successful, having no effect at all.

        Best,
        Max

  5. Hello,

    I found this script very interesting and I want to use it for my needs. But I have a problem because the script is not running automatically when I plug in the USB drive. I run manually the script and it works fine as I want. The only thing I have changed is the user and group name from root to pi and I don’t think that is a problem. I checked out the previous answers about privileges of the udev rules and etc, but it seems that this is not a problem. I am running the script on Raspberry pi 3 Debian Jessy OS. It seems that the udev rules are not working properly for some reasons. Any ideas what is going wrong? Any suggestions or help?

    Thank you,

    Nikos.

    • Hi Nikos,
      well, the script should run on every plugged in or out device. Can you show some logs after a plugging in action, please?
      Does the script work if you change user and group back to root again?

  6. Hi Bastian,

    I think that is a udev rules problem for some reason. The same result with pi and root user, the rules are not working automatically. I can not send you log files because no logs are created when I plug in and out a device! When I run manually the script it works great and logs are produced! The problem seems to be weird…have you tested your script on latest Raspbian Jessie OS with the newest kernel versions?? I saw that the udev rules syntax is different in other occasions…..is the syntax the appropriate in order the rules to be executed?? Could you tell me some ways to test if the rules are called properly or if they are active?? How could I check if the udev rules have syntax problems??

    Thank you,

    Nikos.

  7. Good work!
    To support more than one partition on a drive, change regex to:
    regex='(/dev/sd[a-z][0-9]): LABEL=\”([A-Za-z0-9_\-]+)\”.*TYPE=\”([a-zA-Z0-9]+)\”‘

  8. Hi,

    first, thanks for your script!

    On my pi 3 the script doesn’t work automatic:

    blkid
    /dev/sda1: LABEL=”Daten” UUID=”38E09822E097E502″ TYPE=”ntfs”
    root@mapi:~/mount_manager# ls /media/
    root@mapi:~/mount_manager#

    If I try to execute the script manuel all fine, but the automatic detection doesen’t work on my pi.

    any idea?

    Cheers,
    Marco

  9. I think the failure, that the script dosen’t work for some peaople here is, that the file /etc/udev/rules.d/00-mount_manager must end with .rules?!?
    Can you check this?

    • I renamed the file 00-mount_manager to 00-mount_manager.rules and then executed :
      sudo udevadm control –reload-rules
      Now it works for me on raspberry pi 3.

  10. Problem now is:

    root@mapi:/media# ls -all
    ls: Zugriff auf Daten nicht möglich: Der Socket ist nicht verbunden
    insgesamt 8
    drwxr-xr-x 3 root root 4096 Jan 8 16:34 .
    drwxr-xr-x 22 root root 4096 Jan 3 19:27 ..
    d????????? ? ? ? ? ? Daten

    • Hello Marco,
      I never tried my script on an Raspberry Pi 3. But I just ordered one today and will check it soon.
      Maybe the *.rules is a thing. I will check that. too.

      • Hello Bastian… First off, well done with the script! This is exactly what I was looking for. I used to do this with ArchLinux but could find time doing it on Jessie. Thank you buddy.

        For those who are using Raspberry Pi 3 with the latest Jessie installed, you have to include the .rules so to make the udev rules it has to be:

        sudo nano /etc/udev/rules.d/00-mount_manager.rules

        I do hope that this helps a lot of you guys out there. This is a great tool and I hope they incorporate this with the raspberry pi Jessie OS.

        • Hi Marco,
          I hadn’t had the time to look into it yet. Pretty busy work. :(
          Sorry.
          Have you come any further on your own? What is your current status/error?

          • Hi bastian

            thanks for the script.

            that last error marco got is:
            ls: cannot access (folder): Transport endpoint is not connected

            This happens when the driver loaded by mount continues to run, anything mounted thru fuseblk will do this (when mounted NOT from an interactive shell). This is not how vfat/ext and others work.

            All child processes are killed by udev after X amount of time.

            Particularly this is ntfs, for me specifically ntfs-3g, but may apply to hfsplus (anything that uses fuse to perform mount?)

            I have fix for mount change:
            mount -t ntfs -o rw $2 $MOUNT_DIR$1 >> $LOG_FILE 2>&1 #not tested
            to
            nohup mount -t ntfs -o rw $2 $MOUNT_DIR$1 >> $LOG_FILE 2>&1 &

            Note however that “remove” (umount) now has the error (not after add).
            $> sudo umount /dev/sda(?)
            ^ this works, so does folder

            “nohup” does not solve the “remove” problem here, because “ls” errors when before the “umount” – I have tried “sudo nohup umount”, and upto “sleep 3” after “umount”, but I must be miss reading the script, I cant find the initial “ls” fail

          • The “remove” fix is here (mentioned in my other post, below?), change:
            MOUNT_DIR=/media/
            to
            MOUNT_D=/media
            MOUNT_DIR=$MOUNT_D/

            at the begining of “start main routine”, change:
            for dir in $(ls -d “${MOUNT_DIR}”*); do
            to
            for dir in $(ls -1 “${MOUNT_D}”); do

            NOTE: the error still exists, but only the lower form will populate the array correctly, which allows “umount” withou changes

  11. Hi thank you for you hard work for the script.

    I have a little problem with mounting ntfs (ntfs-3g installed) , I followed the instructions step by step, I have a RPI 3 with jessie installed and it only mounts fat. I added the .rules and the script creates the folder with the label but it is not accessible. Even if I try to run it manually i get this error in the terminal: “Transport endpoint is not connected”

    Please if you can help me it would be great.
    Thanks

  12. Hi bastian,

    is your raspi arrived? Have you checked the error message from january? Today I try your scipt on a new installed raspbian and the error message is the same.

  13. Hi Bastian,
    I’ve just try you script and its working.. but I can not access th mounted drive.. here are the error msg when I access the dir (via cd) “-bash: cd: /media/Noer-Seagate: Transport endpoint is not connected.”

    Any idea what went wrong?

    Thanks,
    Noer

  14. Script didn’t work on RasPi3 running Debian.
    mount doesn’t like uid=pi,gid=pi must be uid=1000,gid=1000 so had to change user,group
    Had to add: nuser=pi, ngroup=pi then
    chown -R $nuser:$ngroup $MOUNT_DIR$plugged_in_item >> $LOG_FILE 2>&1
    after mkdir line as directory was created was still owned by root but mounted by pi
    Also, your comments after mounter{ have S1 and S2 reversed
    Script now works a treat after mods

    • Hi Greg,
      thanks for your additional mail. Now I finally got some time to look though some comments.
      What exacly have you done? Its not exactly clear to me. Can you explain it a little more, please?
      Anyway thanks for the reply! :)

      Cheers!

  15. I am running a RasPi with Debian stretch and a RasPi2 running Jessie Lite (latest as of 5/17)) which runs perfectly (except for ntfs – see below)
    On the RasPi3 the script wouldn’t work due to issues with udev. This solved the problem:
    https://unix.stackexchange.com/questions/204906/linux-mount-command-returns-zero-0-but-not-working
    Scroll to bottom Solution: to get a fix for this. Once I moved udev.service and changed MountFlags it worked on the Jessie install. (ymmv)

    ntfs doesn’t mount on either RasPi using the script. This is because the udev rule runs in a local root environment and ntfs needs a fuseblock process which ends after the rule is done processing, so you end up with a ntfs drive that’s mounted but *not*. My fix for this was to comment out the mount line in the script, copy the script to the pi home directory, and run it with sudo. The udev rule run will create the /media/usblabel directory only then running again as sudo from your home folder will complete the mount process automatically finding the correct info. An extra step but needed for ntfs.

  16. Update to previous post: moving udev.service fixed issues on RasPi3 running Debian, not RasPi2 running Jessie (it mounts vfat just fine)

Leave a Reply

Read previous post:
Hello world!

Welcome to SolvedForHome.com.   With this site we started a longterm project for home electronic solutions like we built it....

Close