#!/bin/bash
#=============================================================================#
# FILE: fvwm-menu-udisks (obdevicemenu ported to FVWM, attempt 2) #
# VERSION: 1.4.0 #
# #
# DESCRIPTION: an Openbox pipe menu for the management of removable media #
# with Udisks ported to FVWM #
# LICENSE: GPL2 #
# AUTHOR: Jamie Nguyen (ported by greatant) #
#=============================================================================#
# Copyright (C) 2011 Jamie Nguyen
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License v2 as published by the
# Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#-------------------------------------#
# CONFIGURATION #
#-------------------------------------#
# {{{
declare udisks="/usr/bin/udisks"
declare filemanager="/usr/bin/Thunar"
declare notify="/usr/bin/notify-send"
declare notify_options="--expire-time=3000"
declare optical_devices="-E ^/dev/sr[0-9]+"
declare removable_devices="-E ^/dev/sd[b-z][0-9]*|^/dev/mmcblk[0-9]+p*[0-9]*"
declare mount_options="--mount-options nosuid,noexec,noatime"
declare -i show_internal=0
declare -i show_notifications=1
declare -i show_optical_device_filename=1
declare -i show_removable_device_filename=1
declare -i fancy_sort=0
declare separator_removable_media="Removable media"
declare separator_optical_media="Optical media"
declare separator_device_info="Device information"
declare info_udisks_failed="Udisks failed."
declare info_no_devices="None"
declare info_mounted="(mounted)"
declare info_no_label="No label"
declare msg_unmount_all_removable="Unmount all removable devices?"
declare msg_unmount_all_optical="Unmount all optical devices?"
declare msg_mounting_device="Mounting"
declare msg_unmounting_device="Unmounting"
declare msg_unmounted="unmounted successfully."
declare msg_ejecting_device="Ejecting"
declare item_eject="Eject"
declare item_info="Info"
declare item_mount="Mount"
declare item_open="Open"
declare item_unmount="Unmount"
declare item_unmount_all="Unmount all"
declare item_device_filename="device:"
declare item_label="label:"
declare item_vendor="vendor:"
declare item_model="model:"
declare item_type="type:"
declare item_media="media:"
declare item_device_size="size (device):"
declare item_partition_size="size (partition):"
declare CONFIGFILE
if [[ -n "${XDG_CONFIG_HOME}" ]]; then
CONFIGFILE="${XDG_CONFIG_HOME}/obdevicemenu/config"
else
CONFIGFILE="${HOME}/.config/obdevicemenu/config"
fi
if [[ ! -f "${CONFIGFILE}" ]]; then
CONFIGFILE="/etc/obdevicemenu.conf"
fi
if [[ -f "${CONFIGFILE}" ]]; then
. "${CONFIGFILE}"
if [[ $? -ne 0 ]]; then
printf '%s\n' "DestroyMenu recreate FvwmMenuUdisks"
printf '%s\n' "AddToMenu FvwmMenuUdisks fvwm-menu-udisks Title"
printf '%s\n' "+ \"Failed to source configuration file\""
exit 1
fi
fi
if [[ ! -f "${udisks}" ]]; then
printf '%s\n' "DestroyMenu recreate FvwmMenuUdisks"
printf '%s\n' "AddToMenu FvwmMenuUdisks fvwm-menu-udisks Title"
printf '%s\n' "+ \"Udisks not installed\""
exit 1
fi
# }}}
#-------------------------------------#
# INTERNAL API #
#-------------------------------------#
# {{{
notify() {
if [[ "${show_notifications}" = "1" ]] && [[ -f "${notify}" ]]; then
if [ -z "${notify_options}" ]; then
$notify "$*"
else
$notify ${notify_options} "$*"
fi
else
$*
fi
}
# functions to retrieve information
info_blank() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*blank:" \
| awk '{print $2}'
}
info_ejectable() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*ejectable:" \
| awk '{print $2}'
}
info_has_media() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*has media:" \
| awk '{print $3}'
}
info_internal() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*system internal:" \
| awk '{print $3}'
}
info_label() {
local info_label=
info_label="$($udisks --show-info ${1} \
| grep -m 1 -w "^ label:" \
| awk '{$1="";gsub(/_/,"");print substr($0, index($0,$2))}')"
if [[ -n "${info_label}" ]]; then
printf '%s\n' "${info_label}"; return
fi
}
info_media() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*media:" \
| awk '{gsub(/_/,"");print $2}'
}
info_model() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*model:" \
| awk '{$1="";gsub(/_/,"");print substr($0, index($0,$2))}'
}
info_mounted() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*is mounted:" \
| awk '{print $3}'
}
info_mountpath() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*mount paths:" \
| awk '{$1="";$2="";print substr($0, index($0,$3))}'
}
info_type() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*type:" \
| awk '{gsub(/_/,"");print $2}'
}
info_vendor() {
$udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*vendor:" \
| awk '{$1="";gsub(/_/,"");print substr($0, index($0,$2))}'
}
convert_size() {
local -i old_size="${1}"
local new_size=
printf '%s\n' "${old_size}" | grep -ow -E ^[1-9]{1}[0-9]*$ >/dev/null 2>&1
[[ $? -ne 0 ]] && return 1
if [[ "${old_size}" -gt "21474836480" ]]; then
new_size="$((${old_size}/1073741824)) GB"
elif [[ "${old_size}" -gt "10485760" ]]; then
new_size="$((${old_size}/1048576)) MB"
elif [[ "${old_size}" -gt "10240" ]]; then
new_size="$((${old_size}/1024)) kB"
else
new_size="${old_size} bytes"
fi
printf '%s\n' "${new_size}"
}
info_device_size() {
local info_device_size=
info_device_size="$($udisks --show-info ${1} \
| grep -m 1 -w "^[[:space:]]*size:" \
| awk '{print $2}')"
if [[ -n "${info_device_size}" ]]; then
info_device_size="$(convert_size "${info_device_size}")"
printf '%s\n' "${info_device_size}"
fi
}
info_partition_size() {
local info_partition_size=
info_partition_size="$($udisks --show-info ${1} \
| grep -m 1 -w " size:" \
| awk '{print $2}')"
if [[ -n "${info_partition_size}" ]]; then
info_partition_size="$(convert_size "${info_partition_size}")"
printf '%s\n' "${info_partition_size}"
fi
}
# functions to perform udisks commands
action_mount() {
local devname="${1}"
notify "$(printf '%s\n' "${msg_mounting_device} ${devname} ...")"
notify "$($udisks --mount ${devname} ${mount_options})"
}
action_unmount() {
local devname="${1}"
notify "$(printf '%s\n' "${msg_unmounting_device} ${devname} ...")"
notify "$($udisks --unmount ${devname})"
# if udisks unmounts successfully, there is no output from udisks
# so notify-send fails and will return 1
if [[ $? = '1' ]]; then
notify "$(printf '%s\n' "${devname} ${msg_unmounted}")"
fi
}
action_unmount_all() {
for devname in $*; do
notify "$(printf '%s\n' "${msg_unmounting_device} ${devname} ...")"
notify "$($udisks --unmount ${devname})"
if [[ $? = '1' ]]; then
notify "$(printf '%s\n' "${devname} ${msg_unmounted}")"
fi
done
}
action_eject() {
local devname="${1}"
local -i mounted=
mounted="$(info_mounted "${devname}")"
if [[ "${mounted}" -eq 1 ]]; then
action_unmount "${devname}"
fi
notify "$(printf '%s\n' "${msg_ejecting_device} ${devname} ...")"
notify "$($udisks --eject ${devname})"
}
action_open() {
local devname="${1}"
local info_mountpath="$(info_mountpath "${devname}")"
if [[ -n "${info_mountpath}" ]]; then
$filemanager "${info_mountpath}"
else
$filemanager
fi
}
action_info() {
local devname="${1}"
local devmajor="${devname%%[0-9]*}"
$udisks --show-info ${devname} >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
printf '%s\n' "DestroyMenu FvwmMenuUdisksInfo"
printf '%s\n' "AddToMenu FvwmMenuUdisksInfo fvwm-menu-udisks Title"
printf '%s\n' "+ \"${info_udisks_failed}\""
exit 0
fi
local info_label="$(info_label "${devname}")"
local info_vendor="$(info_vendor "${devmajor}")"
local info_model="$(info_model "${devmajor}")"
local info_device_size="$(info_device_size "${devmajor}")"
local info_type="$(info_type "${devname}")"
local info_media="$(info_media ${devname})"
if [[ "${devname}" != "${devmajor}" ]]; then
local info_partition_size="$(info_partition_size "${devname}")"
fi
printf '%s\n' "DestroyMenu FvwmMenuUdisksInfo"
printf '%s\n' "AddToMenu FvwmMenuUdisksInfo \"${separator_device_info}\" Title"
[[ -n "${info_device_filename}" ]] && \
printf '%s\n' "+ \"${item_device_filename} ${devname}\""
[[ -n "${info_label}" ]] && \
printf '%s\n' "+ \"${item_label} ${info_label}\""
[[ -n "${info_vendor}" ]] && \
printf '%s\n' "+ \"${item_vendor} ${info_vendor}\""
[[ -n "${info_model}" ]] && \
printf '%s\n' "+ \"${item_model} ${info_model}\""
[[ -n "${info_revision}" ]] && \
printf '%s\n' "+ \"${item_revision} ${info_revision}\""
[[ -n "${info_type}" ]] && \
printf '%s\n' "+ \"${item_type} ${info_type}\""
[[ -n "${info_version}" ]] && \
printf '%s\n' "+ \"${item_version} ${info_version}\""
[[ -n "${info_media}" ]] && \
printf '%s\n' "+ \"${item_media} ${info_media}\""
[[ -n "${info_partition_size}" ]] && \
printf '%s\n' "+ \"${item_partition_size} ${info_partition_size}\""
[[ -n "${info_device_size}" ]] && \
printf '%s\n' "+ \"${item_device_size} ${info_device_size}\""
}
fancy_sort() {
# this is a very hacky way to sort devices so that /dev/sdc11 wont come
# before /dev/sdc2, which happens due to a shortcoming of the sort command
# we wont tell bash that partition_number is a number, otherwise it
# breaks when leading zeros are added
local devname= devmajor= partition_number=
local -i j=
# first lets put a leading zero in front of single digits (sdc1 -> sdc01).
# we are going to ignore /dev/mmcblk* devices... too complicated to sort.
j=0
for devname in ${removable[@]}; do
if [[ "${devname}" =~ ^/dev/dm-[0-9]+ ]]; then
devmajor="${devname%%[0-9]*}"
elif [[ "${devname}" =~ ^/dev/fd[0-9]+ ]]; then
devmajor="${devname%%[0-9]*}"
elif [[ "${devname}" =~ ^/dev/sd[a-z][0-9]+ ]]; then
devmajor="${devname%%[0-9]*}"
else
j=$((++j)); continue
fi
if [[ "${devname}" = "${devmajor}" ]]; then
j=$((++j)); continue
fi
partition_number="${devname#${devmajor}}"
removable[${j}]=${devmajor}$(printf '%02d' "${partition_number}")
j=$((++j))
done
# now the device array can be sorted properly
removable=( $(printf '%s\n' "${removable[@]}" | sort) )
# now lets remove those leading zeros that we added
j=0
for devname in ${removable[@]}; do
if [[ "${devname}" =~ ^/dev/dm-[0-9]+ ]]; then
devmajor="${devname%%[0-9]*}"
elif [[ "${devname}" =~ ^/dev/fd[0-9]+ ]]; then
devmajor="${devname%%[0-9]*}"
elif [[ "${devname}" =~ ^/dev/sd[a-z][0-9]+ ]]; then
devmajor="${devname%%[0-9]*}"
else
j=$((++j)); continue
fi
if [[ "${devname}" = "${devmajor}" ]]; then
j=$((++j)); continue
fi
partition_number="${devname#${devmajor}}"
removable[${j}]=${devmajor}${partition_number#0}
j=$((++j))
done
}
# main functions
device_menu() {
local -a removable=( )
local -a optical=( )
local -a mounted_removable=( )
local -a mounted_optical=( )
local -i j=0
printf '%s\n' "DestroyMenu recreate FvwmMenuUdisks"
printf '%s\n' "AddToMenu FvwmMenuUdisks MissingSubmenuFunction FuncFvwmMenuUdisks"
$udisks --enumerate-device-files >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
printf '%s\n' "+ fvwm-menu-udisks Title"
printf '%s\n' "+ \"${info_udisks_failed}\""
exit 0
fi
# removable media
printf '%s\n' "+ \"${separator_removable_media}\" Title"
removable=( $($udisks --enumerate-device-files \
| grep -ow ${removable_devices} | sort) )
if [[ ${fancy_sort} -eq 1 ]]; then
fancy_sort
fi
for devname in ${removable[@]}; do
local devmajor=
local info_label=
local -i info_internal=
local -i mounted=
local -i partitions=
# check here to see if a device such as /dev/sdb has partitions. if there
# are partitions, such as /dev/sdb1, then hide /dev/sdb from being shown.
if [[ "${devname}" =~ ^/dev/mmcblk[0-9]+p*[0-9]* ]]; then
devmajor="${devname%%p[0-9]*}"
if [[ "${devname}" = "${devmajor}" ]]; then
partitions="$($udisks --enumerate-device-files \
| grep -ow -E ^${devname}p[0-9]+ -c)"
if [[ "${partitions}" -gt 0 ]]; then
continue
fi
fi
else
devmajor="${devname%%[0-9]*}"
if [[ "${devname}" = "${devmajor}" ]]; then
partitions="$($udisks --enumerate-device-files \
| grep -ow -E ^${devname}[0-9]+ -c)"
if [[ "${partitions}" -gt 0 ]]; then
continue
fi
fi
fi
info_internal="$(info_internal "${devmajor}")"
if [[ "${info_internal}" -eq 1 ]] && [[ "${show_internal}" -eq 0 ]]; then
continue
fi
if [[ "${info_internal}" -eq 0 ]] || [[ "${show_internal}" -eq 1 ]]; then
j=$((++j))
info_label="$(info_label "${devname}")"
# if no label is present, then use information about the device instead
if [[ -z "${info_label}" ]]; then
info_label="$(info_label "${devmajor}")"
[[ -z "${info_label}" ]] && info_label="$(info_model "${devmajor}")"
[[ -z "${info_label}" ]] && info_label="$(info_vendor "${devmajor}")"
if [[ -z "${info_label}" ]]; then
info_label="No label"
else
info_label="No label (${info_label})"
fi
fi
mounted="$(info_mounted "${devname}")"
if [[ "${mounted}" -eq 0 ]]; then
if [[ "${show_removable_device_filename}" -eq 1 ]]; then
printf '%s\n' "+ \"${devname#/dev/}: ${info_label}\" Popup ${devname}"
# printf '%s' "<menu id=\"mount${devname}\" "
# printf '%s' "label=\"${devname#/dev/}: ${info_label}\" "
# printf '%s' "execute=\"$0 --mount-menu removable "
# printf '%s' "${devname} ${info_label}\" />"
# printf '\n'
else
printf '%s\n' "+ \"${info_label}\" Popup ${devname}"
# printf '%s' "<menu id=\"mount${devname}\" "
# printf '%s' "label=\"${info_label}\" "
# printf '%s' "execute=\"$0 --mount-menu removable "
# printf '%s' "${devname} ${info_label}\" />"
# printf '\n'
fi
else
if [[ "${show_removable_device_filename}" -eq 1 ]]; then
printf '%s\n' "+ \"${devname#/dev/}: ${info_label} ${info_mounted}\" Popup ${devname}"
# printf '%s' "<menu id=\"mount${devname}\" "
# printf '%s' "label=\"${devname#/dev/}: "
# printf '%s' "${info_label} ${info_mounted}\" "
# printf '%s' "execute=\"$0 --mount-menu removable "
# printf '%s' "${devname} ${info_label}\" />"
# printf '\n'
else
printf '%s\n' "+ \"${info_label} ${info_mounted}\" Popup ${devname}"
# printf '%s' "<menu id=\"mount${devname}\" "
# printf '%s' "label=\"${info_label} ${info_mounted}\" "
# printf '%s' "execute=\"$0 --mount-menu removable "
# printf '%s' "${devname} ${info_label}\" />"
# printf '\n'
fi
mounted_removable[${#mounted_removable[*]}]=${devname}
fi
fi
done
if [[ $j -eq 0 ]]; then
printf '%s\n' "+ \"${info_no_devices}\""
fi
if [[ "${#mounted_removable[*]}" -gt 0 ]]; then
printf '%s\n' "+ \"${item_unmount_all}\" Exec $0 --unmount-all-removable ${mounted_removable[*]}"
fi
# optical media
printf '%s\n' "+ \"${separator_optical_media}\" Title"
optical=( $($udisks --enumerate-device-files \
| grep -ow ${optical_devices} | sort) )
if [[ "${#optical[*]}" -eq 0 ]]; then
printf '%s\n' "+ \"${info_no_devices}\""
return 0
fi
for devname in ${optical[@]}; do
local info_label=
local -i info_blank=
local -i info_has_media=
local -i mounted=
info_has_media="$(info_has_media "${devname}")"
if [[ "${info_has_media}" -eq 0 ]]; then
printf '%s\n' "+ \"${devname#/dev/}: ${info_no_devices}\""
else
info_blank="$(info_blank "${devname}")"
if [[ "${info_blank}" -eq 1 ]]; then
info_label="Blank media"
else
info_label="$(info_label ${devname})"
[[ -z "${info_label}" ]] && info_label="$(info_model "${devname}")"
[[ -z "${info_label}" ]] && info_label="No label"
fi
mounted="$(info_mounted "${devname}")"
if [[ "${mounted}" -eq 0 ]]; then
if [ "${show_optical_device_filename}" -eq 1 ]; then
printf '%s\n' "+ \"${devname#/dev/}: ${info_label}\" Popup ${devname}"
# printf '%s' "<menu id=\"mount${devname}\" "
# printf '%s' "label=\"${devname#/dev/}: ${info_label}\" "
# printf '%s' "execute=\"$0 --mount-menu optical "
# printf '%s' "${devname} ${info_label}\" />"
# printf '\n'
else
printf '%s\n' "+ \"${info_label}\" Popup ${devname}"
# printf '%s' "<menu id=\"mount${devname}\" "
# printf '%s' "label=\"${info_label}\" "
# printf '%s' "execute=\"$0 --mount-menu optical "
# printf '%s' "${devname} ${info_label}\" />"
# printf '\n'
fi
else
if [[ "${show_optical_device_filename}" -eq 1 ]]; then
printf '%s\n' "+ \"${devname#/dev/}: ${info_label} ${info_mounted}\" Popup ${devname}"
# printf '%s' "<menu id=\"mount${devname}\" "
# printf '%s' "label=\"${devname#/dev/}: "
# printf '%s' "${info_label} ${info_mounted}\" "
# printf '%s' "execute=\"$0 --mount-menu optical "
# printf '%s' "${devname} ${info_label}\" />"
# printf '\n'
else
printf '%s\n' "+ \"${info_label} ${info_mounted}\" Popup ${devname}"
# printf '%s' "<menu id=\"mount${devname}\" "
# printf '%s' "label=\"${info_label} ${info_mounted}\" "
# printf '%s' "execute=\"$0 --mount-menu optical "
# printf '%s' "${devname} ${info_label}\" />"
# printf '\n'
fi
mounted_optical[${#mounted_optical[*]}]=${devname}
fi
fi
done
if [[ "${#mounted_optical[*]}" -gt 0 ]]; then
printf '%s\n' "+ \"${item_unmount_all}\" Exec $0 --unmount-all-optical ${mounted_optical[*]}"
fi
}
mount_menu() {
# local media_type="${1}"
local devname="${1}"
# local info_label="${3}"
local devname_short="${devname##*/}"
local -i info_ejectable=
local -i mounted=
printf '%s\n' "DestroyMenu recreate ${devname}"
printf '%s\n' "AddToMenu ${devname} MissingSubmenuFunction FuncFvwmMenuUdisksInfo"
printf '%s\n' "+ DynamicPopdownAction DestroyMenu ${devname}"
printf '%s\n' "+ \"${devname}\" Title"
mounted="$(info_mounted "${devname}")"
if [[ "${mounted}" -eq 0 ]]; then
printf '%s\n' "+ \"${item_open}\" Exec $0 --mount-and-open ${devname}"
# printf '%s\n' "<item label=\"${item_open}\">"
# printf '%s\n' "<action name=\"Execute\">"
# printf '%s\n' "<command>$0 --mount-and-open ${devname}</command>"
# printf '%s\n' "</action>"
# printf '%s\n' "</item>"
printf '%s\n' "+ \"${item_mount}\" Exec $0 --mount-device ${devname}"
# printf '%s\n' "<item label=\"${item_mount}\">"
# printf '%s\n' "<action name=\"Execute\">"
# printf '%s\n' "<command>$0 --mount-device ${devname}</command>"
# printf '%s\n' "</action>"
# printf '%s\n' "</item>"
else
printf '%s\n' "+ \"${item_open}\" Exec $0 --open-directory ${devname}"
# printf '%s\n' "<item label=\"${item_open}\">"
# printf '%s\n' "<action name=\"Execute\">"
# printf '%s\n' "<command>$0 --open-directory ${devname}</command>"
# printf '%s\n' "</action>"
# printf '%s\n' "</item>"
printf '%s\n' "+ \"${item_unmount}\" Exec $0 --unmount-device ${devname}"
# printf '%s\n' "<item label=\"${item_unmount}\">"
# printf '%s\n' "<action name=\"Execute\">"
# printf '%s\n' "<command>$0 --unmount-device ${devname}</command>"
# printf '%s\n' "</action>"
# printf '%s\n' "</item>"
fi
info_ejectable="$(info_ejectable "${devname}")"
if [[ "${info_ejectable}" -eq 1 ]]; then
printf '%s\n' "+ \"${item_eject}\" Exec $0 --eject-device ${devname}"
# printf '%s\n' "<item label=\"${item_eject}\">"
# printf '%s\n' "<action name=\"Execute\">"
# printf '%s\n' "<command>$0 --eject-device ${devname}</command>"
# printf '%s\n' "</action>"
# printf '%s\n' "</item>"
fi
printf '%s\n' "+ \"${item_info}\" Popup FvwmMenuUdisksInfo"
# printf '%s' "<menu id=\"showinfo${devname##*/}\" "
# printf '%s' "label=\"${item_info}\" "
# printf '%s' "execute=\"$0 --show-info ${devname}\" />"
# printf '\n'
action_info ${devname}
}
# }}}
if [[ $# == 0 ]]; then
device_menu
exit 0
fi
case "${1}" in
"--mount-menu")
declare media_type="${2}"
declare devname="${3}"
shift 3
mount_menu ${media_type} ${devname} "${*}"
;;
"--mount-device")
action_mount "${2}"
;;
"--unmount-device")
action_unmount "${2}"
;;
"--eject-device")
action_eject "${2}"
;;
"--open-directory")
action_open "${2}"
;;
"--mount-and-open")
action_mount "${2}" && action_open "${2}"
;;
"--show-info")
action_info "${2}"
;;
"--unmount-all-removable")
shift 1 && action_unmount_all $@
;;
"--unmount-all-optical")
shift 1 && action_unmount_all $@
;;
esac
# vim: set ts=4 sw=4 noet foldmethod=marker :