#!/bin/sh
# Monta solas las memorias USB y tarjetas SD en /mnt/ETIQUETA (o /mnt/sdXY
# si no tienen etiqueta) y las desmonta al sacarlas, avisando con una
# notificación al usuario de la sesión gráfica. Lo lanza runit como root
# (servicio automontaje). Los discos internos no se tocan.

# Usuario dueño de la sesión de dwm, o el primero normal (UID 1000).
usuario() {
	pid=$(pgrep -xo dwm) && stat -c %u "/proc/$pid" || echo 1000
}

# avisar TÍTULO TEXTO: notificación en la sesión gráfica, si la hay
avisar() {
	pid=$(pgrep -xo dwm) || return 0
	nombre=$(getent passwd "$(stat -c %u "/proc/$pid")" | cut -d: -f1)
	entorno=$(tr '\0' '\n' <"/proc/$pid/environ" |
		grep -E '^(DISPLAY|DBUS_SESSION_BUS_ADDRESS|XDG_RUNTIME_DIR)=')
	# shellcheck disable=SC2086
	env $entorno chpst -u "$nombre" notify-send -i drive-removable-media \
		"$1" "$2" >/dev/null 2>&1 &
}

montar() {
	dev=$1 tipo=$2 etiqueta=$3
	findmnt -rn -S "$dev" >/dev/null && return

	dir=/mnt/$(printf %s "$etiqueta" | tr -c 'A-Za-z0-9._-' _)
	if [ "$dir" = /mnt/ ] || mountpoint -q "$dir" ||
		{ [ -d "$dir" ] && [ -n "$(ls -A "$dir")" ]; }; then
		dir=/mnt/${dev#/dev/}
	fi

	# Los sistemas de archivos de Windows no guardan dueños: se montan a
	# nombre del usuario para que pueda escribir sin sudo.
	uid=$(usuario)
	gid=$(getent passwd "$uid" | cut -d: -f4)
	opts=nosuid,nodev,noatime
	case $tipo in
	vfat) opts=$opts,uid=$uid,gid=$gid,umask=022,utf8,flush ;;
	exfat) opts=$opts,uid=$uid,gid=$gid,umask=022 ;;
	ntfs) tipo=ntfs3 opts=$opts,uid=$uid,gid=$gid,umask=022 ;;
	esac

	mkdir -p "$dir"
	if mount -t "$tipo" -o "$opts" "$dev" "$dir"; then
		avisar "Memoria montada" "$dir"
	else
		rmdir "$dir" 2>/dev/null
		avisar "No se pudo montar" "$dev ($tipo)"
	fi
}

desmontar() {
	dir=$(findmnt -rn -o TARGET -S "$1") || return 0
	case $dir in /mnt/*) ;; *) return ;; esac
	umount -l "$dir" && rmdir "$dir" 2>/dev/null
	avisar "Memoria desconectada" "$dir"
}

limpiar() {
	accion='' dev='' bus='' uso='' tipo='' etiqueta=''
}

# Lee eventos de udev (bloques KEY=VALOR separados por una línea vacía).
procesar() {
	limpiar
	while IFS= read -r linea; do
		case $linea in
		ACTION=*) accion=${linea#*=} ;;
		DEVNAME=*) dev=${linea#*=} ;;
		ID_BUS=*) bus=${linea#*=} ;;
		ID_FS_USAGE=*) uso=${linea#*=} ;;
		ID_FS_TYPE=*) tipo=${linea#*=} ;;
		ID_FS_LABEL=*) etiqueta=${linea#*=} ;;
		'')
			case $bus:$dev in usb:* | *:/dev/mmcblk*) ;; *) dev='' ;; esac
			if [ -n "$dev" ]; then
				case $accion in
				add | change) [ "$uso" = filesystem ] &&
					montar "$dev" "$tipo" "$etiqueta" ;;
				remove) desmontar "$dev" ;;
				esac
			fi
			limpiar
			;;
		esac
	done
}

{
	# Primero lo que ya está conectado, luego los eventos nuevos
	for d in /sys/class/block/*; do
		echo ACTION=add
		udevadm info --query=property --path="$d"
		echo
	done
	exec udevadm monitor --udev --property --subsystem-match=block
} | procesar
