USB Automount Script for Raspberry Pi
When 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.
This is quite a cool bit of code. Manual mounting sucks
I liked this idea, so I tried it out.
Unfortunately nothing happens. Is there anything else I have to do to make it work?
I can mount/umount my usb hdd manually and it works fine. But not this automated method.
Hi Petter,
what does the log file say?
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
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.
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?
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 Max,
I don’t know this at hand, but I ll check.
Does the script work with other USB devices for you?
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
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?
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.
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]+)\”‘
Thanks. I added the [0-9] to the script.
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
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.
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.
Hey bastian,
Is your pi arrived I am very interested in your solution!
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
THANKS ALOT Paul for those tow fixes!!!
I updated the post accordingly.
Never got NTFS Mount to work – always Transport endpoint error even with the hohup added to beg of line and using ntfs-3g. FAT32 and ext4 mounts work fine creating and deleting folders under /media.
Greg, you can use nohup from the command line, NOTE that my fix in the script backgrounds the task. The reason the script fails to remove is the difference between how ‘ls’ handles ‘/path’ and ‘path/ – re read my fixes, they do work 100%
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
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.
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
Thanks you just what i needed
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!
you need to use the user settings as uid=1000,gid=1000, because:
a) this is the 1st user account created
b) even some Raspbian systems DO NOT have user ‘pi’ (eg PipaOS)
if it is set to user id it will work on almost any system, at least for the default (non-root) user
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.
Hi Greg!
I’ve the same problem but I don’t understand how you modify original script to make it works.. I think it would useful if you write another post with the updated script..
Update to previous post: moving udev.service fixed issues on RasPi3 running Debian, not RasPi2 running Jessie (it mounts vfat just fine)
the only problem I have with this script since the ntfs fix, is when you have a multi card reader mounted. because it boots with devices installed, it only triggers partition add/remove, not device add/remove. I still dont have a suitable solution to this.
Note: my current workaroiund is running the mount script as sudo manually, which does add/remove partitions
Hello World! I just want to ask, with this you need to login as root in order to use it properly.. right?