#!/bin/sh -e

##########################################################################
#   Synopsis:
#       devd-mount [/dev/]partition
#
#   Description:
#       .B devd-mount
#       is a script for mounting external media on FreeBSD.  It is
#       normally invoked by /usr/local/etc/devd/devd-mount.conf,
#       which is run by the devd(8) service upon creation of
#       a new device node such as /dev/da0.  This typically occurs
#       when a USB drive is inserted, for example.
#
#       The device name should contain a filesystem recognized
#       by FreeBSD or by fusefs, such as EXFAT, FAT32, FAS16, NTFS,
#       ISO 9660 (CDs), UDF (DVDs), of UFS (FreeBSD native).
#
#       The mount point for a device is /media/device-name, e.g.
#       /dev/da0p1 is mounted on /media/da0p1.
#
#       The configuration file /usr/local/etc/devd-mount.conf
#       can be used to control
#       ownership and permissions on the mount point, and the program
#       invoked upon a successful mount.  Defaults are shown below.
#       
#       .nf
#       .na
#       MEDIA_MANAGER=/usr/local/bin/qmediamanager
#       GROUP=operator
#       MODE=775
#       .ad
#       .fi
#
#       The auto-automount-setup(1) script, part of sysutils/auto-admin
#       will configure a FreeBSD system to use devd-mount.
#       
#   Arguments:
#       device  Name of device node, with or without /dev/ prefix
#       
#   Returns:
#       0 upon successful mount, non-zero error codes otherwise
#
#   Examples:
#       devd-mount da0p1
#
#   Files:
#       /usr/local/etc/devd-mount.conf
#       /usr/local/etc/devd/devd-mount.conf
#
#   See also:
#       devd(8), mount(8), qmediamanager(1), npmount(1)
#       
#   History:
#   Date        Name        Modification
#   2023-07-01  J Bacon     Derived from devpub-mount
##########################################################################

usage()
{
    printf "Usage: $0 [/dev/]partition\n"
    exit 1
}


##########################################################################
#   Function description:
#       Launch chosen media manager program on the local X11 display
#       
#   History:
#   Date        Name        Modification
#   2023-06-15  J Bacon     Begin
##########################################################################

media_manager()
{
    if [ $# != 3 ]; then
	printf "Usage: media_manager mount-point partition fstype\n" >> /dev/stderr
	exit 1
    fi
    mount_point=$1
    device=$2
    fs_type=$3
    
    # This fails under SDDM:
    # DISPLAY_ID=$(ps -ax -p `pgrep Xorg` | fgrep libexec/Xorg | awk '{ print $6 }')
    # ps -axw -p `pgrep Xorg` | fgrep libexec/Xorg
    # 1413  -  S    0:09.75 /usr/local/libexec/Xorg -nolisten tcp -auth /var/run/sddm/{f5f14042-f00a-4038-9de7-94f125def9a0} -background 
    DISPLAY_ID=:0
    # Debug
    printf "DISPLAY_ID = $DISPLAY_ID\n" >> $log
    
    # Build command to run following mount
    : ${MEDIA_MANAGER:="qmediamanager mount-point device fstype"}
    no_tabs=$(echo "$MEDIA_MANAGER" | tr '\t' ' ')
    cmd=$(echo "$no_tabs" | cut -d ' ' -f 1)
    for arg in $(echo "$no_tabs" | cut -d ' ' -f 2-); do
	echo $arg
	case $arg in
	mount-point)
	    cmd="$cmd $mount_point"
	    ;;
	
	device)
	    cmd="$cmd $device"
	    ;;
	
	fs-type)
	    cmd="$cmd $fs_type"
	    ;;
	
	*)
	    cmd="$cmd $arg"
	    ;;
	esac
    done
    
    printf "cmd = $cmd\n"
    printf "Running $cmd\n" >> $log
    
    ps -aexwwj | grep "DISPLAY=$DISPLAY_ID" >> $log
    display_users=$(ps -aexwwj | grep "DISPLAY=$DISPLAY_ID" | awk '{ print $1 }' | sort -u)
    printf "display_users = $display_users\n" >> $log
    for user in $display_users; do
	printf "Trying su -l $user -c env DISPLAY=$DISPLAY_ID $cmd\n" >> $log
	if su -l $user -c "env DISPLAY=$DISPLAY_ID $cmd"; then
	    break;
	fi
    done
}


try_mount()
{
    partition=$1
    mount_point=/media/${partition#/dev/}
    if [ ! -e $mount_point ]; then
	mkdir -p $mount_point
    fi
    
    # FIXME: chown and chgrp don't work on many filesystems
    # Find a way to set ownership and perms
    if [ -n "$GROUP" ]; then
	chgrp $GROUP $mount_point
    fi
    if [ -n "$MODE" ]; then
	chmod $MODE $mount_point
    fi
    
    ##########################################################################
    #   Determining filesystem type is not straightforward.  The fstyp
    #   command is extremely limited.  We use it where we can and will
    #   have to resort to other hacks or simply try mounting in other
    #   cases, though failed mounts seem to generate more devd events,
    #   leading to multiple devd-mount processes for the same device.
    ##########################################################################
    
    printf "$partition $mount_point PID=$$\n" >> $log
    
    # FIXME:
    # devd notification seems to come before the device is ready to mount
    # Find a better way to determine when the the device is ready
    sleep 2
    
    # Note: Failed mount attempts appear to trigger new notifications
    # Use fstyp as much as possible
    
    if [ "$(fstyp $partition)" = "exfat" ] && /usr/local/sbin/mount.exfat $partition $mount_point 2>> $log; then
	printf "Mounted exfat.\n" >> $log
	media_manager $mount_point $partition exfat 2>> $log
    elif [ "$(fstyp $partition)" = "msdosfs" ] && /sbin/mount_msdosfs $partition $mount_point 2>> $log; then
	printf "Mounted FAT.\n" >> $log
	media_manager $mount_point $partition fat 2>> $log
    elif [ "$(fstyp $partition)" = "cd9660" ] && /sbin/mount_cd9660 $partition $mount_point 2>> $log; then
	printf "Mounted cd9660.\n" >> $log
	media_manager $mount_point $partition cd9660 2>> $log
    elif [ "$(fstyp $partition)" = "ufs" ] && /sbin/mount $partition $mount_point 2>> $log; then
	printf "Mounted UFS.\n" >> $log
	if [ -n "$GROUP" ]; then
	    chgrp $GROUP $mount_point
	fi
	if [ -n "$MODE" ]; then
	    chmod $MODE $mount_point
	fi
	media_manager $mount_point $partition ufs 2>> $log
    elif [ "$(fstyp $partition)" = "ntfs" ] && /sbin/ntfs-3g $partition $mount_point 2>> $log; then
	printf "Mounted NTFS.\n" >> $log
	media_manager $mount_point $partition ntfs 2>> $log
    elif /usr/local/bin/hfsdump $partition 2>> $log && /usr/local/bin/hfsfuse $partition $mount_point 2>> $log; then
	printf "Mounted Apple HFS.\n" >> $log
	media_manager $mount_point $partition "Apple-HFS" 2>> $log
    elif /sbin/mount_udf $partition $mount_point 2>> $log; then
	printf "Mounted UDF.\n" >> $log
	media_manager $mount_point $partition udf 2>> $log
    # fstyp currently shows ext2fs for ext4
    # Not sure if we can rely on this for the future, but without
    # checking we end up with multiple failed lklfuse processes
    elif [ "$(fstyp $partition)" = "ext2fs" ] && /usr/local/bin/lklfuse \
	    -o allow_other -o type="ext4" -o intr -o uid=0 -o gid=0 \
	    -o umask=002 $partition $mount_point 2>> $log; then
	printf "Mounted ext4.\n" >> $log
	media_manager $mount_point $partition ext4 2>> $log
    else
	printf "Could not mount $partition.\n" >> $log
	# Notify user about normal partitions (da0p1, da0s1) that
	# cannot be mounted.
	if echo $partition | grep -Eq "da[0-9]+[a-z]+[0-9]+"; then
	    media_manager $mount_point $partition "unknown"
	fi
	return 1
    fi
}


##########################################################################
#   Main
##########################################################################

if [ $# != 1 ]; then
    usage
fi
partition=$1
if ! echo $partition | grep -q '^/dev/'; then
    partition=/dev/$partition
fi

log=/var/log/devd-mount/${partition#/dev/*}
mkdir -p $(dirname $log)
rm -f $log
printf "Args: $@\n" > $log

conf_file=/usr/local/etc/devd-mount.conf
if [ -e $conf_file ]; then
    . $conf_file
fi
try_mount $partition
