IgnorantGuru's Blog

Linux software, news, and tips

A Custom udisks Automounter

UPDATE: Please see the new version of this script. The information below is outdated.

Below is my initial attempt to write my own auto-mounting script for DVD and removable drives using udisks. On Arch Linux with Openbox, there is no auto-mounting by default so you have to setup your own. I’ve used autofs for awhile, but it is unreliable (ignores some devices entirely, and with other devices sometimes it works, sometimes not). I see some things like udiskie, but why load python just for that? So I thought I would explore what udisks can do.

udisks seems to have some relationship to udev and policykit – it is a replacement for HAL. It includes a command-line tool that easily lets normal users monitor, mount, and unmount devices. It seems pretty trouble free thus far – when you use the command-line tool, the daemon automatically starts, so there is no configuration or udev/rules.d involved. Just install udisks from your package manager.

To monitor changes to drives:

udisks --monitor

Monitoring activity from the disks daemon. Press Ctrl+C to cancel.
changed:     /org/freedesktop/UDisks/devices/sr0
job-changed: /org/freedesktop/UDisks/devices/sr0
changed:     /org/freedesktop/UDisks/devices/sr0

Anytime drives are added, removed, or a DVD is inserted, the change will be shown.

To get info on a drive (is it mounted, is it mountable, etc):

udisks --show-info /dev/sr0

To mount a drive (my DVD drive in this example):

udisks --mount /dev/sr0

To unmount a drive:

udisks --unmount /dev/sr0

This gave me all I needed to build my own custom auto-mounter in bash. One advantage to doing it this way is that I can directly make it do whatever I want when changes are detected. I decided that I wanted it to mount DVDs and USB devices instantly, without prompt, and open the drive in a tab in my file manager.

First, I would start a monitor and send the output to a file:

udisks --monitor > /tmp/udisks-monitor.log

Leaving that running, I can then watch for any changes to that file using inotifywait (requires installation of inotifytools):

inotifywait /tmp/udisks-monitor.log

That will quit when the file changes (passing execution to the rest of the script). The script can then read /tmp/udisks-monitor.log to see what drive changed, check the ––show-info for that drive, and take suitable actions.

This devmon script is what I came up with:
UPDATE: Please see the new version of this script. The information below is outdated.

#!/bin/bash

# REQUIRES: udisks inotifytools zenity

monfile=/tmp/udisks-monitor.log
devmon="$0" # pathname of this script
devmonname="$(basename "$devmon")"  # name of this script

startmon()
{
	test=`ps -Af | grep -v grep | grep "$devmonname --monitor"`
	if [ "$test" = "" ]; then
		echo "startmon"
		$devmon --monitor &
	fi
}

killmon()
{
	monpid=`ps -eo pid,cmd | grep -v grep | grep -m 1 "$devmonname --monitor" \
	        | sed 's/\([0-9 ]*\).*/\1/'`
	if [ "$monpid" != "" ]; then
		echo "killmon"
		pkill -P $monpid # kill udisks owned
		kill $monpid
	fi
}

mountdrive()  # $1=drive
{
	# mount drive $1
	echo "devmon mount $1"
	test=`udisks --mount $1 --mount-options noexec,nosuid | \
	      sed 's/^Mounted .* at \(.*\)/\1/'`
	err="$?"
	if [ "$err" = "0" ] && [ "$test" != "" ]; then
		# open file manager at new drive
		pcmanfm-mod -t "$test" &
	fi
}

case "$1" in
	"--watch" )
		# watch udisks log for changes
		sleep 1
		startmon
		sleep 1
		while [ 1 ]; do
			# check udisks log for change
			test=`stat -c %Y "$monfile"`
			if [ "$test" != "" ]; then
				if [ "$test" != "$timedisk" ]; then
					timedisk="$test"
					echo "udisks-monitor $(date +%H:%M:%S)"
					test=`ps -Af | grep -v grep | grep "$devmonname --change"`
					if [ "$test" = "" ]; then
						echo "devmon --change"
						$devmon --change &
					fi
				fi
			else
				echo "missing monfile - restart"
				killmon
				sleep 1
				killmon
				sleep 1
				startmon
				sleep 1
			fi
			# wait for change to $monfile
			inotifywait -t 300 -q -e modify -e moved_to -e create \
								  -e delete $monfile
		done
		;;
	"--start" )
		# start udisks monitoring of drives
		sleep 1
		startmon
		exit
		;;
	"--stop" )
		# stop udisks monitoring of drives
		killmon
		sleep 1
		killmon
		sleep 1
		rm -f "$monfile"
		exit
		;;
	"--restart" )
		# restart udisks monitoring of drives
		echo "devmon restart"
		killmon
		sleep 1
		killmon
		sleep 1
		rm -f "$monfile"
		sleep 2
		startmon
		exit
		;;
	"--monitor" )
		# call udisks	
		udisks --monitor > $monfile   # stays running
		exit
		;;
	"--change" )
		# run when changed is detected
		startmon
		sleep .3
		# $monfile recently change?
		test=`find $monfile -cmin -1`
		if [ "$test" != "" ]; then
			l=`tail -n 1 $monfile`
			if [ "$l" = "changed:     /org/freedesktop/UDisks/devices/sr0" ]; then
				# DVD
				test=`udisks --show-info /dev/sr0 | grep -c \
				      -e "^  optical disc:" \
					  -e "    blank:                     0" \
					  -e "  usage:                       filesystem" \
					  -e "  is mounted:                  0"`
				if [ "$test" = "4" ]; then
					mountdrive /dev/sr0
				fi
			elif [ "${l:0:6}" = "added:" ]; then
				# USB?
				drive=${l##*/}
				if [ "${drive:0:3}" = "sdd" ] || [ "${drive:0:3}" = "sde" ] \
				   || [ "${drive:0:3}" = "sdf" ]; then
					if [ "${#drive}" = "3" ]; then
						# select first partition?
						test=`cat /proc/partitions | grep " ${drive}1$"`
						if [ "$test" != "" ]; then
							drive="${drive}1"
						fi
					fi
					test=`udisks --show-info /dev/$drive | grep -c \
					      -e "  is mounted:                  0" \
						  -e "  usage:                       filesystem" \
						  -e "  has media:                   1"`
					if [ "$test" = "3" ]; then
						mountdrive /dev/$drive
					fi
				fi
			else
				# need to restart mon to limit log file size?
				test=`find $monfile -cmin +300`  # older than 5 hours?
				if [ "$test" != "" ]; then
					echo "devmon monitor restart - older than 5 hours"
					killmon
					sleep 1
					rm -f "$monfile"
					sleep 2
					startmon
				fi
			fi
		fi
		exit
		;;
	"--unmount" )
		# unmount all removables
		alldrives="sdd1 sde1 sdf1 sdd sde sdf"
		drives=""
		for drive in $alldrives; do
			test=`mount | grep "^/dev/$drive "`
			if [ "$test" != "" ]; then
				drives="$drives $drive"
			fi
		done
		if [ "$drives" = "" ]; then
			msg="No removable drives mounted"
		else
			msg="Unmounting$drives..."
		fi
		zenity --info --title="Unmount" --text="$msg" &
		zpid=$!
		sync
		for drive in $alldrives; do
			test=`mount | grep "^/dev/$drive "`
			if [ "$test" != "" ]; then
				udisks --unmount /dev/$drive
			fi
		done
		sync
		sleep 2
		kill $zpid
		exit
		;;
esac

exit

A few important things to notice about this script:

  • It requires: udisks inotifytools zenity
  • It is hard-coded for /dev/sr0 and my removable drives start at /dev/sdd. It looks for changes to sr0, sdd, sde, and sdf only. (This can easily be changed in two places – the $alldrives line and the “if [ ${drive:0:3} = sdd ]” line.
  • It uses pcmanfm-mod as the file manager to open on a new mount – this can easily be changed

This script is daemon, monitor, and client all in one, depending on how you run it.

To start things off:

devmon --watch

That will do several things. It will start a process “devmon ––monitor”, which will in turn start “udisks ––monitor > /tmp/udisks-monitor.log”, both of which will keep running in the background.

It will then start “inotifywait /tmp/udisks-monitor.log”, which will watch for changes to that file (and thus detect drive changes such as CD and USB insertions). When a change to that file is detected, it will run “devmon ––change” to process the change. One item to note is that the script tries to insure that only one instance of ––monitor is running, and only one instance of ––change is running at any given time.

To process the change, the script will first make sure the monitor file /tmp/udisks-monitor.log has changed in the last minute (to make sure its fresh). If so, it will use tail to read the last line of that file. It uses that line to determine what drive changed, and then calls “udisks ––show-info” to find the status of the drive. If the drive has filesystem content and is unmounted, it will mount the drive and then open the file manager.

By default, udisks mounts drives to /media using /media/cd for the DVD drive and a name based on the volume label for USB drives. I don’t know how to change this behavior, but it works well enough for me thus far as is.

The script can also unmount all USB drives – I associate this with a shortcut key:

devmon --unmount

One thing to note about udisks: The way it mounts/unmount, when you press the eject button on the DVD drive, the disk will come out regardless of what’s going on. If you happen to be copying a file from the disc, it won’t stop it. I actually like it like this. In the case of USB drives, which aren’t read-only, I use my unmount shortcut key to sync and unmount them before unplugging.

To stop the watch process, in another console issue:

devmon --stop

which stops the ––monitor daemon and its udisks monitor child process. Then stop the ––watch process. This insures that you don’t leave the udisks monitor running. You can also use:

killall devmon devmon.sh udisks

Also note that this script contains ––start ––stop and ––restart options. However, these are only useful if you have another script doing the watching and running “devmon ––change”, as I have on my system (I have a custom daemon that watches a bunch of things on the system and takes various actions, so I use this instead of “devmon ––watch”). Otherwise you won’t need these options.

So far this script is working like a charm – much more reliable than autofs and I like having direct control over what happens. The only problem I have thus far is that sometimes pcmanfm-mod won’t start if its not already running – I’m not sure why that is but I’ll update this if I figure it out. It’s probably an expired environment variable.

You can find more info on udisks in the Arch Wiki, including other wrappers such as udiskie (which I didn’t try).

January 26, 2011 - Posted by | Scripts, Tips

2 Comments

  1. I spent yesterday trying to get automounting of CDs to work, and came up with something similar in spirit to what you have here. (Though using a pipe instead of a file.)

    And I’ve come to the conclusion that udisks is seriously deficient, given that polling and parsing text output is such a crude mechanism. (You can also talk to it via DBUS messages though.)

    But with udev you can easily link an event to a script or program, which is how I’ve implemented automount for USB sticks. There is no such mechanism in udisks.

    Incidentally, my script didn’t quite work right! The udisks polling of sr0 for media isn’t reliable on my system for some reason. If I manually execute “udisks –poll-for-media /dev/sr0” it works.

    Comment by sggraham | February 15, 2011

    • Please see the updated version of devmon as the version above was more of an experiment. The new version needs neither a file or a pipe – uses a bash 4 coprocess.

      udisks seems to be doing a good job thus far – give the new version of devmon a try. I find it easier and more reliable than messing with udev.

      Comment by igurublog | February 15, 2011


Sorry, the comment form is closed at this time.