#!/bin/bash
# Script Name: devmon    http://igurublog.wordpress.com/downloads/script-devmon/ 
# Requires:    udevil bash>=4        Recommended: eject spacefm|zenity
# Optional:    udisks v1 may be substituted for udevil
#              If udevil is not set suid, udisks v1/v2 or pmount is required
# License: GNU GENERAL PUBLIC LICENSE Version 3 http://www.gnu.org/licenses/gpl-3.0.txt
# Thanks to Bernard Baeyens (berbae) for code from udisksvm script

#=========================================================================

defaultmountoptions="noexec,nosuid,nodev,noatime"

#=========================================================================

help()
{
	cat << EOF
devmon version 1.1.8  (distributed with udevil)
Automounts and unmounts optical and removable drives
Requires:    udevil bash>=4        Also Recommended: eject spacefm|zenity
Optional:    udisks v1 may be substituted for udevil
             If udevil is not set suid, udisks v1/v2 or pmount is required
Usage: devmon [AUTOMOUNT-OPTIONS]  # Run as daemon to automount
       devmon [MOUNT-OPTIONS]      # Or run as client to manually un/mount
AUTOMOUNT-OPTIONS:  (these can be used only in daemon mode)
--exec-on-device DEVICE "COMMAND"  Execute COMMAND after mounting DEVICE
--exec-on-label "LABEL" "COMMAND"  Execute COMMAND after mounting LABEL
--exec-on-video "COMMAND"          Execute COMMAND after video DVD mount
--exec-on-audio "COMMAND"          Execute COMMAND after audio CD insertion
--exec-on-disc  "COMMAND"          Execute COMMAND after data CD/DVD mount
--exec-on-drive "COMMAND"          Execute COMMAND after drive mount
--exec-on-unmount "COMMAND"        Execute COMMAND after unmount
--exec-on-remove  "COMMAND"        Execute COMMAND after drive removal
  Where the following in COMMAND will be replaced with:
     %d    mount point directory (eg /media/cd)
     %f    device name (eg /dev/sdd1)
     %l    label of mounted volume
  Multiple --exec-on-XXX options may be used to execute multiple commands.
  Other exec-on-XXX commands are ignored if exec-on-device or -label executed.
--mount-options "OPTIONS"          Default: $defaultmountoptions
--info-on-mount                    Show mounted drive info in a popup dialog
--no-mount                         Don't mount anything, just exec (disables
                                   --exec-on-video)
--no-unmount                       Don't unmount all removable drives on exit

MOUNT-OPTIONS:  (these can be used only in client mode)
--unmount-removable | -r      Sync and unmount all removable drives and show
                              pop-up dialog (spacefm or zenity required)
--unmount-recent | -c         Unmount most recently mounted removable drive
--unmount-optical | -o        Unmount all optical drives (error pop-up only)
--unmount-all | -u            Same as --unmount-removable --unmount-optical
--unmount DIR|DEVICE          Unmount DEVICE or mount point DIR
--eject DIR|DEVICE            Unmount and eject DEVICE or mount point DIR
--mount-all | -a              Mount all removable and optical drives
--mount DEVICE                Mount DEVICE
--mount-options|--mount-fstype|--eject-options "OPTIONS"
                              These options will be passed to mount or eject
--enumerate-device-files      List known devices

UNIVERSAL OPTIONS:  (these can be used in both daemon and client modes)
--ignore-device DEVICE        Ignore DEVICE (eg /dev/sdd1)
--ignore-label "LABEL"        Ignore volume with LABEL
--sync | -s                   Add sync mount option for ext2-4 ntfs ufs, or
                              flush for fat & vfat (slower writing but safer)
--internal                    Also attempt to un/mount internal system drives
                              (this is mostly a fix for esata issues)
--no-gui | -g                 Do not show spacefm/zenity pop-up dialogs
Instructions and updates:
  http://igurublog.wordpress.com/downloads/script-devmon/
EOF
	exit
}

test2()
{
	if [ "${2:0:1}" = "-" ] || [ "$2" = "" ]; then
		echo "devmon: Option $1 requires an argument" 1>&2
		exit 1
	fi	
}

test3()
{
	if [ "${2:0:1}" = "-" ] || [ "$2" = "" ] || \
	   [ "${3:0:1}" = "-" ] || [ "$3" = "" ]; then
		echo "devmon: Option $1 requires two arguments" 1>&2
		exit 1
	fi	
}

unknown()
{
	echo "devmon: Unknown option $1" 1>&2
	echo "        For help use: devmon --help" 1>&2
	exit 1
}

# parse command line
execoix=0
execomx=0
execovx=0
execoax=0
execodx=0
execolx=0
execoux=0
execorx=0
umntx=0
mntx=0
ejx=0
igdevx=0
iglabx=0
while [ "$1" != "" ]; do
	if [ "${1:0:1}" = "-" ]; then
		case "$1" in
			--help )
				help
				exit
				;;
			# don't use eval on these to preserve command strings
			--exec-on-drive )
				test2 "$1" "$2"
				execoi[$execoix]="$2"
				(( execoix++ ))
				shift
				;;
			--exec-on-disc )
				test2 "$1" "$2"
				execom[$execomx]="$2"
				(( execomx++ ))
				shift
				;;
			--exec-on-video )
				test2 "$1" "$2"
				execov[$execovx]="$2"
				(( execovx++ ))
				shift
				;;
			--exec-on-audio )
				test2 "$1" "$2"
				execoa[$execoax]="$2"
				(( execoax++ ))
				shift
				;;
			--exec-on-device )
				test3 "$1" "$2" "$3"
				execod1[$execodx]="$2"
				execod2[$execodx]="$3"
				(( execodx++ ))
				shift 2
				;;
			--exec-on-label )
				test3 "$1" "$2" "$3"
				execol1[$execolx]="$2"
				execol2[$execolx]="$3"
				(( execolx++ ))
				shift 2
				;;
			--exec-on-unmount )
				test2 "$1" "$2"
				execou[$execoux]="$2"
				(( execoux++ ))
				shift
				;;
			--exec-on-remove )
				test2 "$1" "$2"
				execor[$execorx]="$2"
				(( execorx++ ))
				shift
				;;
			--info-on-mount )
				infomount=1
				;;
			--no-mount )
				nomount=1
				;;
			--sync )
				syncopt=1
				;;
			--unmount-on-exit )
			    # leave for usage compat with versions prior to 1.0.1
				;;
			--no-unmount )
				nounmount=1
				;;
			--unmount-all )
				unmountrem=1
				unmountoptical=1
				;;
			--unmount-removable )
				unmountrem=1
				;;
			--unmount-optical )
				unmountoptical=1
				;;
			--unmount-recent )
				unmountrecent=1
				;;
			--unmount )
				test2 "$1" "$2"
				umnt[$umntx]="$2"
				(( umntx++ ))
				shift
				;;
			--mount-all )
				mountall=1
				;;
			--mount )
				test2 "$1" "$2"
				mnt[$mntx]="$2"
				(( mntx++ ))
				shift
				;;
			--eject )
				test2 "$1" "$2"
				ej[$ejx]="$2"
				(( ejx++ ))
				shift
				;;
			--mount-options )
				test2 "$1" "$2"
				mountoptions="$2"
				shift
				;;
			--mount-fstype )
				test2 "$1" "$2"
				mountfstype="$2"
				shift
				;;
			--eject-options )
				test2 "$1" "$2"
				ejectoptions="$2"
				shift
				;;			
			--internal )
				internal=1
				;;
			--nogui | --no-gui )
				nogui=1
				;;
			--ignore-device )
				test2 "$1" "$2"
				igdev[$igdevx]="$2"
				(( igdevx++ ))
				shift
				;;				
			--ignore-label )
				test2 "$1" "$2"
				iglab[$iglabx]="$2"
				(( iglabx++ ))
				shift
				;;
			--enumerate-device-files )
				all=`grep " [ms]d[a-z0-9]*$" /proc/partitions | \
									sed 's/.* \([ms]d[a-z0-9]*\)/\/dev\/\1/'`
				n=$'\n'
				if [ "$all" != "" ]; then
					all="$all$n"
				fi
				for f in /dev/mapper/*; do
					if [ -h "$f" ]; then
						name="$(basename "$f")"
						target="$(readlink "$f")"
						target="$(basename "$target")"
						if [ "$target" != "" ]; then
							all="$all/dev/$target$n"
							if [ "${name:0:4}" = "loop" ]; then
								all="$all/dev/$name$n"
							fi
						fi
					fi
				done
				# add mounted loop devices (attached requires losetup as root)
				loop=`cat /proc/self/mountinfo 2>/dev/null | grep ' /dev/loop[0-9]* ' | cut -f9 -d ' ' 2>/dev/null`
				if [ "$loop" != "" ]; then
					loop="$loop$n"
				fi
				other=`/bin/ls -1 /dev/md[0-9]* /dev/dm-* /dev/sr* 2>/dev/null`
				if [ "$other" != "" ]; then
					other="$other$n"
				fi
				all="$all$loop$other"
				echo -n "$all" | sort | uniq
				exit
				;;
			--* )
				unknown "$1";;
			-* )
				o="${1:1}"
				while [ "$o" != "" ]; do
					case "${o:0:1}" in
						r )
							unmountrem=1;;
						o )
							unmountoptical=1;;
						u )
							unmountrem=1
							unmountoptical=1
							;;
						a )
							mountall=1;;
						s )
							syncopt=1;;
						c )
							unmountrecent=1;;
						g )
							nogui=1;;
						h )
							help
							exit
							;;
						* )
							unknown "-${o:0:1}";;
					esac
					o="${o:1}"
				done
				;;
		esac
	else
		unknown "$1"
	fi
	shift
done
(( mountmode = umntx + mntx + ejx + unmountrem + unmountoptical + mountall + unmountrecent ))

# Warnings
if [ "$(whoami)" = "root" ]; then
	echo "WARNING: running devmon as root is usually not required or recommended" 1>&2
fi

if (( execoix + execomx + execovx + execoax + execodx + execolx + execoux + execorx \
	  + nounmount + infomount != 0 )) && (( mountmode != 0 )); then
	echo "WARNING: devmon automount options ignored in mount mode" 1>&2
fi

# Discovery
udevil=`which udevil 2>/dev/null`
udisks1=`which udisks 2>/dev/null`
udisks2=`which udisksctl 2>/dev/null`
pmount=`which pmount 2>/dev/null`
zenity=`which zenity 2>/dev/null`
spacefm=`which spacefm 2>/dev/null`

if [ "$udevil" != "" ] && [ -x "$udevil" ]; then
	info_cmd="$udevil"
elif [ "$udisks1" != "" ] && [ -x "$udisks1" ]; then
	info_cmd="$udisks1"
else
	echo "WARNING: devmon can't run udevil or udisks v1 for device monitoring" 1>&2
	info_cmd="udevil"
fi

if [ "$udevil" != "" ] && [ -x "$udevil" ]; then
	if [ -u "$udevil" ] || [ "$(whoami)" = "root" ]; then
		mount_cmd="$udevil"
	fi
fi

if [ "$mount_cmd" = "" ] && [ "$pmount" != "" ] && [ -x "$pmount" ]; then
	if [ -u "$pmount" ] || [ "$(whoami)" = "root" ]; then
		mount_cmd="$pmount"
	fi
fi

if [ "$mount_cmd" = "" ] && [ "$udisks1" != "" ] && [ -x "$udisks1" ]; then
	mount_cmd="$udisks1"
fi

if [ "$mount_cmd" = "" ] && [ "$udisks2" != "" ] && [ -x "$udisks2" ]; then
	mount_cmd="$udisks2"
fi

if [ "$mount_cmd" = "" ]; then
	echo "WARNING: devmon can't run udevil, pmount, or udisks for mounting" 1>&2
	mount_cmd="udevil"
fi


driveinfo()    #$1=dev    #Optional $2=quiet 
{
	unset systeminternal usage ismounted presentationnopolicy hasmedia \
			opticaldisc numaudiotracks type partition media blank label
	uinfos=`$info_cmd --show-info $1 2> /dev/null`
	label=`echo "$uinfos" | grep -m 1 "^  label:" | sed 's/ *label: *\(.*\)/\1/'`
	listinfos=`echo "$uinfos" | grep \
				-e "^  system internal:" \
				-e "^  usage:" \
				-e "^  type:" \
				-e "^  is mounted:" \
				-e "^  presentation nopolicy:" \
				-e "^  has media" \
				-e "^  optical disc:" \
				-e "  blank:" \
				-e "  num audio tracks:" \
				-e "^  partition:" \
				-e "  media:"`
	# The change for type= is to take only its first value in listinfos
	listinfos=$(echo "$listinfos" | sed 's/ //g
				s/:/=/
				s/opticaldisc=/&1/
				s/type=\(.*\)/type=${type:-\1}/
				s/[()]//g
				s/partition=/&1/')
	eval "$listinfos"
	if (( internal == 1 )); then
		systeminternal="ignored"
	fi
	# Take only the first character
	hasmedia=${hasmedia:0:1}
	# If "partition:" not find in listinfos, should mean it is not a partition
	partition=${partition:-0}
	nopolicy="$presentationnopolicy"
	if (( mountmode == 0 )) && [ "$systeminternal" != "1" ] && [ "$2" != "quiet" ]; then
		if [ "$usage" = "filesystem" ] || [ "$opticaldisc" = "1" ]; then
			echo "device: [$1]"
			echo "    systeminternal: [$systeminternal]"
			echo "    usage:          [$usage]"
			echo "    type:           [$type]"
			echo "    label:          [$label]"
			echo "    ismounted:      [$ismounted]"
			echo "    nopolicy:       [$nopolicy]"
			echo "    hasmedia:       [$hasmedia]"
			echo "    opticaldisc:    [$opticaldisc]"
			echo "    numaudiotracks: [$numaudiotracks]"
			echo "    blank:          [$blank]"
			echo "    media:          [$media]"
			echo "    partition:      [$partition]"
		fi
	fi
}

ignoredevice()
{
	idx=0
	while (( idx < igdevx )); do
		if [ "$1" = "${igdev[$idx]}" ]; then
			echo "devmon: ignored device $1"
			return 0
		fi
		(( idx++ ))
	done
	return 1
}

ignorelabel()
{
	ilx=0
	while (( ilx < iglabx )); do
		if [ "$1" = "${iglab[$ilx]}" ]; then
			echo "devmon: ignored label $1"
			return 0
		fi
		(( ilx++ ))
	done
	return 1
}

execcommands()  # $exectype "${exec[@]}"
{
	exectype="$1"
	shift
	while [ "$1" != "" ]; do
		usercmd="$1"
		usercmd="${usercmd//%f/$dv}"
		usercmd="${usercmd//%l/$lb}"
		usercmd="${usercmd//%d/$point}"
		if [ "$usercmd" != "" ]; then
			echo "devmon: [$exectype] eval $usercmd &"
			eval $usercmd &
		fi
		shift
	done
}

mountdev()   # $1=device  [$2=label]  [$3=devtype or fstype]
{
	# set label comment
	if [ "$2" = "" ]; then
		lblcmt=""
	else
		lblcmt="# ($2)"
	fi
	# set mount-fstype option
	if [ "$mountfstype" != "" ]; then
		if [ "$mount_cmd" = "$udisks2" ] || [ "$mount_cmd" = "$pmount" ]; then
			fst="-t"
		else
			fst="--mount-fstype"
		fi
	else
		fst=""
	fi
	# set default mount options
	if [ "$mountoptions" != "" ]; then
		mopts="$mountoptions"
	else
		mopts="$defaultmountoptions"
	fi
	# set sync mount options
	if (( syncopt == 1 )); then
		case "$3" in
			ext2 | ext3 | ext4 | ufs | ntfs )
				mopts="$mopts,sync";;
			fat | vfat )
				mopts="$mopts,flush";;
		esac
	fi

	if [ "$mount_cmd" = "$udisks2" ]; then
		mntmsg="devmon: $mount_cmd mount -b $1 -o \"$mopts\" $fst $mountfstype  $lblcmt"
	elif [ "$mount_cmd" = "$pmount" ]; then
		mntmsg="devmon: $mount_cmd $fst $mountfstype $1  $lblcmt"
	else
		mntmsg="devmon: $mount_cmd --mount $1 --mount-options \"$mopts\" $fst $mountfstype  $lblcmt"
	fi
	if [ "$3" != "nofs" ]; then
		echo "$mntmsg" 
	fi
	
	# mount
	if [ "$mount_cmd" = "$udisks2" ]; then
		umsg=`$mount_cmd mount -b $1 -o "$mopts" $fst $mountfstype 2>&1`
	elif [ "$mount_cmd" = "$pmount" ]; then
		umsg=`$mount_cmd $fst $mountfstype $1 2>&1`
	else
		umsg=`$mount_cmd --mount $1 --mount-options "$mopts" $fst $mountfstype 2>&1`
	fi
	mounterr="$?"
	# get mount point
	point=`echo "$umsg" | grep "^Mounted " | sed 's/^Mounted .* at \(.*\)/\1/'`

	if [ "$mount_cmd" = "$pmount" ] && [ "$point" = "" ]; then
		# pmount doesn't output the mount point - get it below
		point=" "
	fi
	if [ "$mounterr" != "0" ] || [ "$point" = "" ]; then
		# if $3=nofs then there was no apparent filesystem but we tried to mount it 
		# anyway in case it didn't report the filesystem accurately, so ignore the
		# error
		if [ "$3" != "nofs" ]; then
			echo "$umsg" 1>&2
			echo "devmon: error mounting $1 ($mounterr)" 1>&2
			if [ "$mount_cmd" = "$udisks1" ] || [ "$mount_cmd" = "$udisks2" ]; then
				if (( mountmode == 0 )) && (( polkiterrgiven != 1 )) && \
				           [ "$(echo "$umsg" | grep 'Not *Authorized')" != "" ]; then
					if (( nogui != 1 )); then
						if [ "$spacefm" != "" ]; then
							( sleep 3 && $spacefm -g --title "devmon error" --window-icon error --label "udisks functions are not authorized through policykit,\nso devmon cannot automount drives.\nPlease install udevil or see devmon's consolekit installation instructions:\n\nhttp://igurublog.wordpress.com/downloads/script-devmon/#install\n\n(To silence this pop-up add --no-gui to devmon's command line.)" --button "Open Website" xdg-open "http://igurublog.wordpress.com/downloads/script-devmon/#install" --button ok &> /dev/null ) &							
						else
							( sleep 3 && WINDOWID="" $zenity --error --no-wrap --title="devmon error" \
							  --text="udisks functions are not authorized through policykit,\nso devmon cannot automount drives.\nPlease install udevil or see devmon's consolekit installation instructions:\n\nhttp://igurublog.wordpress.com/downloads/script-devmon/#install\n\n(To silence this pop-up add --no-gui to devmon's command line.)" &> /dev/null ) &
						fi
					fi
					echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 1>&2
					echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 1>&2
					echo "udisks functions are not authorized through policykit," 1>&2
					echo "so devmon cannot automount drives." 1>&2
					echo "Please see devmon's consolekit installation instructions:" 1>&2
					echo "http://igurublog.wordpress.com/downloads/script-devmon/#install" 1>&2
					echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 1>&2
					echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 1>&2	
					polkiterrgiven=1
				fi
			fi
		fi
		uerr=3
		return 3
	elif [ "$3" = "nofs" ]; then
		# no filesystem reported but successful mount
		echo "$mntmsg"
	fi
	
	# set devtag for recognizing mounted by devmon
	devpath="$1"
	devtag="${devpath#/dev/}"
	devtag="${devtag//-/_}"
	eval devmounted${devtag}=1
	
	echo "$umsg"
	if [ "$point" = " " ]; then
		# pmount doesn't output the mount point - get it now
		sleep 1
		point=`$info_cmd --show-info $1 2>/dev/null | grep "^  mount paths:" | sed 's/.*: *\(.*\)/\1/'`
		point="${point%%, *}"
	fi
}

unmountdev()   # $1=device
{
	if [ "$mount_cmd" = "$udisks2" ]; then
		echo "devmon: $mount_cmd unmount -b $1"
		uerrmsg=`$mount_cmd unmount -b $1 2>&1`
	elif [ "$mount_cmd" = "$pmount" ]; then
		echo "devmon: pumount $1"
		uerrmsg=`pumount $1 2>&1`
	else
		echo "devmon: $mount_cmd --unmount $1"
		uerrmsg=`$mount_cmd --unmount $1 2>&1`
	fi
	umounterr="$?"
	if [ "$mount_cmd" = "$udisks1" ] && [ "$uerrmsg" != "" ]; then
		# bug: udisks v1 returns $?==0 when unmount fails	
		umounterr=1
	fi
	if [ "$uerrmsg" != "" ]; then
		echo "$uerrmsg"
	fi
	if [ $umounterr -ne 0 ]; then 
		uerr=3
		return 3
	fi
}

ejectdev()
{
	echo "devmon: eject $ejectoptions $1"
	eject $ejectoptions $1
	if [ "$?" != "0" ]; then
		uerr=3
		return 3
	fi
}

mountdrive()   # $1=device $2=label  [$3=devtype or fstype]
{
	dv="$1"
	lb="$2"
	tp="$3"
	unset point

	if ( ignoredevice "$dv" ) || ( ignorelabel "$lb" ); then
		return
	fi

	# mount
	if [ "$tp" != "audiocd" ] && (( nomount != 1 )); then
		mountdev $dv "$lb" "$tp"
		if [ "$?" != "0" ]; then
			return
		fi
	fi

	# exec on device
	unset execdone
	x=0
	while (( x < execodx )); do
		if [ "${execod1[$x]}" = "$dv" ]; then
			usercmd="${execod2[$x]}"
			usercmd="${usercmd//%f/$dv}"
			usercmd="${usercmd//%l/$lb}"
			usercmd="${usercmd//%d/$point}"
			if [ "$usercmd" != "" ]; then
				echo "devmon: [exec on device] eval $usercmd"
				eval $usercmd &
				execdone=1
			fi
		fi
		(( x++ ))
	done
	if (( execdone == 1 )); then return; fi

	# exec on label
	x=0
	while (( x < execolx )); do
		if [ "${execol1[$x]}" = "$lb" ]; then
			usercmd="${execol2[$x]}"
			usercmd="${usercmd//%f/$dv}"
			usercmd="${usercmd//%l/$lb}"
			usercmd="${usercmd//%d/$point}"
			if [ "$usercmd" != "" ]; then
				echo "devmon: [exec on label] eval $usercmd"
				eval $usercmd &
				execdone=1
			fi
		fi
		(( x++ ))
	done
	if (( execdone == 1 )); then return; fi

	# exec on video
	if [ "$tp" = "dvd" ] && [ "$point" != "" ] && [ -d "$point/VIDEO_TS" ]; then
		echo "devmon: videodvd $dv ($lb) on $point"
		if (( execovx != 0 )); then
			execcommands "exec on video" "${execov[@]}"
		fi
		return
	fi

	# exec on audio
	if [ "$tp" = "audiocd" ]; then
		echo "devmon: audiocd $dv ($lb)"
		if (( execoax != 0 )); then
			execcommands "exec on audio" "${execoa[@]}"
		fi
		return
	fi

	# exec on disc
	if [ "$tp" = "optical" ] || [ "$tp" = "dvd" ]; then
		if [ "$point" != "" ] || (( nomount == 1 )); then
			if (( execomx != 0 )); then
				execcommands "exec on disc" "${execom[@]}"
			fi
		fi
		return
	fi

	# exec on drive
	if [ "$point" != "" ] || (( nomount == 1 )); then
		if (( execoix != 0 )); then
			execcommands "exec on drive" "${execoi[@]}"
		fi
	fi

	# info on mount
	if [ "$point" != "" ] && (( infomount == 1 )) && (( nomount != 1 )); then
		sleep .5
		echo "devmon: [info on mount] $dv"
		if (( nogui != 1 )); then
			if [ "$spacefm" != "" ]; then
				$spacefm -g --title "devmon mount" --window-icon drive-removable-media --label \
				"The following device has been mounted:\n\n$(df -hT "$dv" | grep "$dv" | \
				awk '{print "Device:\\t"$1"\x0AType:\\t"$2"\nSize:\\t\\t"$3"\nUsed:\\t"$4"\n""Avail:\\t"$5"\nUse%:\\t"$6"\nMount:\\t"$7,$8,$9,$10}')\nLabel:\\t$lb" \
				--button open spacefm "$point" --button ok &>/dev/null &
			else
				WINDOWID="" $zenity --info --text="The following device has been mounted:\n\n$(df -hT "$dv" \
			   | grep "$dv" | awk '{print "Device:\\t"$1"\x0AType:\\t"$2"\nSize:\\t\\t"$3"\nUsed:\\t"$4"\n""Avail:\\t"$5"\nUse%:\\t"$6"\nMount:\\t"$7,$8,$9,$10}')\nLabel:\\t$lb" --title="devmon mount" &
			fi
		fi
		df -hT "$dv"
	fi
}

mountalldrives()
{
	# Mount all optical drives, no exec
	x=0
	while [ -e /dev/sr$x ]; do
		driveinfo /dev/sr$x
		if [ "$numaudiotracks" = "" ]; then
			numaudiotracks=0
		fi
		if [ "$systeminternal" != "1" ] && [ "$opticaldisc" = "1" ] && \
		   [ "$ismounted" != "1" ] && [ "$hasmedia" != "0" ] && \
		   [ "$blank" != "1" ] && (( numaudiotracks == 0 )) && \
		   [ "$nopolicy" != "1" ]; then
			if ( ignoredevice "/dev/sr$x" ) || ( ignorelabel "$label" ); then
				(( x++ ))
				continue
			fi
			mountdev /dev/sr$x "$label"
			eval notejectedsr$x=1
		fi
		(( x++ ))
	done
	# Mount removable drives, no exec
	IFSOLD="$IFS"
	IFS=$'\n'
	partlist=`grep " [ms]d[a-z0-9]*$" /proc/partitions | sed 's/.* \([ms]d[a-z0-9]*\)/\1/'`
	for p in $partlist; do
		if ( ignoredevice "/dev/$p" ); then
			continue
		fi
		driveinfo /dev/$p
		if ( ignorelabel "$label" ); then
			continue
		else
			if [ "$systeminternal" != "1" ] && [ "$opticaldisc" != "1" ] && \
					   [ "$ismounted" = "0" ] && [ "$nopolicy" != "1" ]; then
				if [ "$usage" = "filesystem" ]; then
					mountdev /dev/$p "$label" "$type"
				else
					mountdev /dev/$p "$label" nofs
				fi
			fi
		fi
	done
	IFS="$IFSOLD"	
}

trapexit()
{
	kill $COPROC_PID 2> /dev/null

	# prevent trap code from executing multiple times on different signals
	if (( trapdone != 1 )); then
		trapdone=1
		# Unmount All
		if (( nounmount != 1 )); then
			IFSOLD="$IFS"
			IFS=$'\n'
			uerr=0
			partlist=`grep " [ms]d[a-z0-9]*$" /proc/partitions | \
			          sed 's/.* \([ms]d[a-z0-9]*\)/\1/'`
			for p in $partlist; do
				if ( ignoredevice "/dev/$p" ); then
					continue
				fi
				driveinfo /dev/$p
				if ( ignorelabel "$label" ); then
					continue
				else
					if [ "$systeminternal" != "1" ] && [ "$opticaldisc" != "1" ] && \
							   [ "$usage" = "filesystem" ] && [ "$ismounted" = "1" ]; then
						if [ "$mount_cmd" = "$udisks2" ]; then
							echo "devmon: [on exit] $mount_cmd unmount -b /dev/$p &"
							$mount_cmd unmount -b /dev/$p &
						elif [ "$mount_cmd" = "$pmount" ]; then
							echo "devmon: [on exit] pumount /dev/$p &"
							pumount /dev/$p &
						else
							echo "devmon: [on exit]$mount_cmd --unmount /dev/$p &"
							$mount_cmd --unmount /dev/$p &
						fi
						if [ "$?" != "0" ]; then
							uerr=3
						fi
					fi
				fi
			done
			IFS="$IFSOLD"
		fi
		echo 'devmon: stopped'
		exit $uerr
	fi
}

# Client Mode
if (( mountmode != 0 )); then
	uerr=0
	if (( unmountrem == 1 )) || (( unmountrecent == 1 )); then
		y=0
		unset udrive zpid
		IFSOLD="$IFS"
		IFS=$'\n'
		if (( unmountrem == 1 )); then
			# Unmount All Removable Drives
			partlist=`grep " [ms]d[a-z0-9]*$" /proc/partitions | sed 's/.* \([ms]d[a-z0-9]*\)/\1/'`
			msgtitle="devmon unmount"
		else
			# Unmount Recent
			partlist=`mount | grep "^/dev/.* on " | sed 's/^\/dev\/\(.*\) on .*/\1/' \
			          | grep -v -e "null" -e "shm" -e "mapper" -e "snd" \
			          -e "video" -e "random" | tac`
			msgtitle="devmon unmount recent"
		fi
		for p in $partlist; do
			if ( ignoredevice "/dev/$p" ); then
				continue
			fi
			driveinfo /dev/$p
			if ( ignorelabel "$label" ); then
				continue
			else
				if [ "$systeminternal" != "1" ] && [ "$opticaldisc" != "1" ] \
						   && [ "$ismounted" = "1" ]; then
					udrive[$y]="/dev/$p"
					(( y++ ))
					if (( unmountrem != 1 )); then break; fi
				fi
			fi
		done
		IFS="$IFSOLD"
		if (( y == 0 )); then
			msg="No removable drives are mounted"
			echo "$msg"
		else
			msg="Unmounting ${udrive[@]}...\n\n(This dialog will close when the devices are unmounted)"
			echo "Preparing to unmount ${udrive[@]}"
		fi
		if (( nogui != 1 )); then
			if [ "$spacefm" != "" ]; then
				spacefm -g --title "$msgtitle" --label "$msg" --button close &> /dev/null &
			else
				WINDOWID="" zenity --info --title="$msgtitle" --text="$msg" &> /dev/null &
			fi
			zpid=$!
		fi
		if (( y > 0 )); then
			echo "devmon: sync"
			sync
			for d in ${udrive[@]}; do
				unmountdev $d
				if [ "$?" != "0" ] && (( nogui != 1 )); then
					driveinfo "$d" quiet
					if [ "$label" = "" ]; then
						lb=""
					else
						lb=" ($label)"
					fi
					msg="Unmount error on $d$lb:\n\n$uerrmsg"
					if [ "$spacefm" != "" ]; then
						spacefm -g --title "$msgtitle" --window-icon error \
						           --label "$msg" --button close &> /dev/null &
					else
						WINDOWID="" zenity --error --title="$msgtitle" --text="$msg" &> /dev/null &
					fi
				fi	
			done
			echo "devmon: sync"
			sync
		fi
		if [ "$zpid" != "" ]; then
			sleep 2
			kill $zpid 2> /dev/null
		fi
	fi

	# Unmount Optical
	if (( unmountoptical == 1 )); then
		x=0
		while [ -e "/dev/sr$x" ]; do
			if ( ignoredevice "/dev/sr$x" ); then
				(( x++ ))
				continue
			fi
			driveinfo /dev/sr$x
			if ( ignorelabel "$label" ); then
				(( x++ ))
				continue
			else
				if [ "$systeminternal" != "1" ] && [ "$opticaldisc" = "1" ] && \
						   [ "$ismounted" = "1" ]; then
					unmountdev /dev/sr$x
					if [ "$?" != "0" ] && (( nogui != 1 )); then
						if [ "$label" = "" ]; then
							lb=""
						else
							lb=" ($label)"
						fi
						msg="Unmount error on /dev/sr$x$lb:\n\n$uerrmsg"
						if [ "$spacefm" != "" ]; then
							spacefm -g --title "devmon unmount optical" \
							           --window-icon error \
							           --label "$msg" --button close &> /dev/null &
						else
							WINDOWID="" zenity --error --title="devmon unmount optical" \
						                   --text="$msg" &> /dev/null &
						fi
					fi	
				fi
			fi
			(( x++ ))
		done
	fi

	# Unmount DIR|DEVICE
	if (( umntx > 0 )); then
		x=0
		while (( x < umntx )); do
			d="${umnt[$x]}"
			# remove trailing slash
			if [ "$d" != "/" ]; then
				d="${d%/}"
			fi
			if [ "${d:0:5}" = "/dev/" ]; then
				# Unmount DEVICE
				unmountdev "$d"
			else
				# Unmount DIR
				if [ "$(dirname "$d")" = "." ]; then
					if [ -d "$(pwd)/$d" ]; then
						d="$(pwd)/$d"
					elif [ -d "/media/$d" ]; then
						d="/media/$d"
					elif [ -e "/dev/$d" ] && [ "$(mount | grep "^/dev/$d on ")" != "" ]; then
						unmountdev "/dev/$d"
						(( x++ ))
						continue
					fi
				fi
				if [ ! -d "$d" ]; then
					echo "devmon: No such directory or mounted device $d" 1>&2
					uerr=3
				else
					dv=`mount | grep -m 1 " on $d type " | awk '{print $1}'`
					if [ "$dv" = "" ]; then
						echo "devmon: Nothing mounted on $d (mtab)" 1>&2
						uerr=3
					else
						unmountdev "$dv"
					fi
				fi
			fi
			(( x++ ))
		done
	fi

	# Eject DIR|DEVICE
	if (( ejx > 0 )); then
		x=0
		while (( x < ejx )); do
			d="${ej[$x]}"
			# remove trailing slash
			if [ "$d" != "/" ]; then
				d="${d%/}"
			fi
			dv=""
			if [ "${d:0:5}" = "/dev/" ]; then
				# Eject DEVICE
				dv="$d"
			else
				# Eject DIR
				if [ "$(dirname "$d")" = "." ]; then
					if [ -d "$(pwd)/$d" ]; then
						d="$(pwd)/$d"
					elif [ -d "/media/$d" ]; then
						d="/media/$d"
					elif [ -e "/dev/$d" ] && [ "$(mount | grep "^/dev/$d on ")" != "" ]; then
						dv="/dev/$d"
					fi
				fi
				if [ "$dv" = "" ]; then
					if [ ! -d "$d" ]; then
						echo "devmon: No such directory or mounted device $d" 1>&2
						uerr=3
					else
						dv=`mount | grep -m 1 " on $d type " | awk '{print $1}'`
						if [ "$dv" = "" ]; then
							echo "devmon: Nothing mounted on $d (mtab)" 1>&2
							uerr=3
						fi
					fi
				fi
			fi
			if [ "$dv" != "" ]; then
				driveinfo "$dv"
				if [ "$systeminternal" != "1" ] && [ "$opticaldisc" = "1" ] && \
				   [ "$ismounted" = "1" ]; then
					unmountdev "$dv"
				fi
				ejectdev "$dv"
			fi
			(( x++ ))
		done
	fi
	
	# Mount DEVICE
	if (( mntx > 0 )); then
		x=0
		while (( x < mntx )); do
			d="${mnt[$x]}"
			# remove trailing slash
			if [ "$d" != "/" ]; then
				d="${d%/}"
			fi
			if [ "$(dirname "$d")" = "." ] && [ "${d:0:5}" != "/dev/" ]; then
				d="/dev/$d"
			fi
			driveinfo "$d" quiet
			if [ "$opticaldisc" = "1" ]; then
				mountdev $d "$label"
			else
				mountdev $d "$label" "$type"
			fi
			(( x++ ))
		done	
	fi

	# Mount All Unmounted
	if (( mountall == 1 )); then
		mountalldrives
	fi

	exit $uerr
fi

# Daemon Mode
if [ "$mountfstype" != "" ]; then
	echo "WARNING: --mount-fstype ignored in daemon mode" 1>&2
	mountfstype=""
fi
if [ "$ejectoptions" != "" ]; then
	echo "WARNING: --eject-options ignored in daemon mode" 1>&2
	ejectoptions=""
fi
pidcount=`ps h -C "${0//*\//}" -o pid | wc -l`
if (( pidcount > 2 )); then
	echo
	echo "WARNING: multiple instances of devmon appear to be running (my pid=$$):"
	ps h -C "${0//*\//}"
	echo
fi

if [ "$mount_cmd" = "$udisks1" ] || [ "$info_cmd" = "$udisks1" ]; then
	# Trigger udisks daemon start two ways
	if [ -e /dev/sr0 ]; then
		udisks --poll-for-media /dev/sr0
	fi
	udisks --show-info /dev/sda > /dev/null
fi
sleep 2 # helps successful sr0 startup mount on reboot


# Startup Mounting
if (( nomount != 1 )); then
	mountalldrives
fi

# Start monitoring
coproc $info_cmd --monitor
err=$?
trap trapexit EXIT SIGINT SIGTERM SIGQUIT
trap "echo devmon: ignored HUP" SIGHUP

if [ $err != "0" ] || [ ! ps -p $COPROC_PID &>/dev/null ]; then
	echo "devmon: unable to start $info_cmd --monitor" 1>&2
	exit 2
fi


# Monitoring Loop
while ps -p $COPROC_PID &>/dev/null; do
	read -u ${COPROC[0]}
	echo "==========================================="
	echo "$REPLY"
	event="${REPLY%:*}"
	devpath="${REPLY#*:}"
	devpath="/dev/${devpath##*/}"
	devtag="${devpath#/dev/}"
	devtag="${devtag//-/_}"
    if [ "$event" != "" ] && [ "$devpath" != "/dev/" ] && [ -e "$devpath" ]; then
		case $event in
			added )
				driveinfo $devpath
				if [ "$systeminternal" != "1" ] && [ "$ismounted" = "0" ] && \
				   [ "$nopolicy" != "1" ]; then
				    if [ "$usage" = "filesystem" ]; then
						mountdrive $devpath "$label" "$type"
					else
						mountdrive $devpath "$label" nofs
					fi
				fi
				;;
			job-changed )
				;;
			removed )
				;;
			changed )
				driveinfo $devpath
				eval notejected=\$notejected${devtag}
				eval devmounted=\$devmounted${devtag}
				eval devmounted${devtag}="$ismounted"
				# If notejected==1 then cd has not been ejected and was probably
				# manually unmounted, so don't automount it.  Otherwise
				# devmon will instantly mount any manual unmount.
				if [ "$systeminternal" != "1" ] && [ "$opticaldisc" = "1" ] && \
				   [ "$ismounted" != "1" ] && [ "$hasmedia" != "0" ] && \
				   [ "$blank" != "1" ] && (( notejected != 1 )) && \
				   [ "$nopolicy" != "1" ]; then
					if [ "$media" = "optical_dvd" ]; then
						mountdrive $devpath "$label" dvd
					elif (( numaudiotracks > 0 )); then
						mountdrive $devpath "$label" audiocd
					else
						mountdrive $devpath "$label" optical
					fi
					eval notejected${devtag}=1
				else
					if [ "$systeminternal" != "1" ] && \
													[ "$hasmedia" = "0" ]; then
						if (( notejected == 1 )) || [ "$notejected" == "" ]; then
							# disc ejected
							echo "devmon: $devpath eject detected"
							eval notejected${devtag}=0
						fi
						if [ "$ismounted" != "0" ]; then
							# unmount if ejected disc still mounted
							unmountdev "$devpath"
						fi
					fi
					if [ "$systeminternal" != "1" ] && [ "$ismounted" != "1" ] && \
							[ "$nopolicy" != "1" ] && [ "$devmounted" = "1" ]; then
						# exec-on-unmount
						if (( execoux != 0 )); then
							if ( ! ignoredevice "$devpath" ) && \
												( ! ignorelabel "$label" ); then
								unset lb point
								dv="$devpath"
								execcommands "exec on unmount" "${execou[@]}"
							fi
						fi
						# remove udevil mount point in case unmounted by other
						if [ "$mount_cmd" = "$udevil" ]; then
							echo "devmon: $udevil clean"
							$udevil clean
						fi
					fi
				fi
			;;
		esac
    elif [ "$event" = "removed" ]; then
		eval unset devmounted${devtag}
		# unmount if removed device is still mounted
		grep -qs "^${devpath} " /proc/mounts &> /dev/null
		mounterr=$?
		if [ $mounterr -ne 0 ]; then
			grep -qs "^${devpath} " /etc/mtab &> /dev/null
			mounterr=$?
		fi
		if [ $mounterr -eq 0 ]; then
			unmountdev "${devpath}"
		elif [ "$mount_cmd" = "$udevil" ]; then
			# remove udevil mount point in case unmounted by other
			echo "devmon: $udevil clean"
			$udevil clean
		fi

		# exec-on-remove
		if (( execorx != 0 )); then
			if ( ! ignoredevice "$devpath" ); then
				unset lb point
				dv="$devpath"
				execcommands "exec on remove" "${execor[@]}"
			fi
		fi
    fi
done

exit

# CHANGELOG
# For changes after version 1.1.0, see udevil ChangeLog
#
# 1.1.0: added support for udevil, udisks2, pmount
# 1.0.5: --exec-on-unmount now executes only once per unmount
# 1.0.4: added --exec-on-unmount, --exec-on-remove
#        added multiple instance warning
#        %f device spec no longer passed in quotes to commands
# 1.0.3: help updated for sync
#        corrected exec-on-drive bug introduced in 1.0.1
# 1.0.2: --sync adds sync for ntfs
# 1.0.1: added --sync
#        added --no-unmount; unmount-on-exit is now default
#        obey UDISKS_PRESENTATION_NOPOLICY to inhibit automount
#        added unmount-all error pop-ups
# 1.0.0: added --unmount-recent
#        improved udisks mount info to stdout
#        improved mount/unmount without apparent filesystem
# 0.9.5: ignore trailing slash in un/mount dir/dev specs
#        attempt to mount no filesystem in case there is one
# 0.9.4: polkit error more selective
# 0.9.3: corrected problems with spaces in volume labels
#        corrected un/mounting of partitionless devices
#        corrected --ignore-devices problem
#        added not authorized pop-up error
# 0.9.2: added --internal
# 0.9.1: corrected --unmount-on-removable not recognized
# 0.9.0: changed --unmount-all to --unmount-removable
#        added --unmount-all (now includes optical)
#        added --unmount-optical
#        added --unmount DIR|DEVICE
#        added --eject DIR|DEVICE
#        added --mount-all
#        added --mount DEVICE
#        added --info-on-mount
#        added pass options to udisks
#        better error msg from udisks
#        individual drive eject detection
#        no limit on number of optical drives
# 0.8.2: more verbose errors
#        run as root changed to warning
# 0.8.1: added --unmount-on-exit
#        adjusted start daemon trigger, timing
#        added 'do not run as root' catcher
#        added trap to ignore SIGHUP

