As mentioned before, I set up RDP with real certs on my RDP hosts. I also frequently access those from my linux-laptop, and the way I had been doing that up until today was "ctrl-r"-ing through my bash history for xfreerdp. I decided to put an end to that today, with something that could be called from within dmenu.  

#!/bin/sh

USER=josh
PASSWORD=""

# first, figure out which host we're accessing.
case $1 in
  host1)
    HOST=host1.joshgordon.net
    ;;
  host2)
    HOST=host2.joshgordon.net
    ;;
  *)
    zenity --warning --text="Invalid host"
    exit 2
    ;;
esac

# get the password
xfrdp_res=1
# 0 = exited from linux
# 11 = disconnected
# 12 = signed off
while [ $xfrdp_res -ne 0 ] && [ $xfrdp_res -ne 11 ] && [ $xfrdp_res -ne 12 ]; do
  # prompt for the psasword from the user
  PASSWORD=$(zenity --password)
  res=$?
  # catch the password prompt being cancelled
  if [ $res -ne 0 ]; then
    echo "Cancelled"
    exit 1
  fi
  # try xfreerdp with the user/password pair 
  xfreerdp /v:"$HOST" /u:"$USER" /p:"$PASSWORD" /f /d:WORKGROUP
  xfrdp_res=$?
done

Alright, great. I can now type rdp host1 and get asked for a password. If I hit cancel, it doesn't re-prompt me, if I quit xfreerdp, it doesn't re-prompt me, but if I type my password wrong, it'll re-prompt me and retry. But there's just one small problem now.

I'm lazy.

I have to type out the whole host1 (which has been shortened for this example to omit real hostnames). I'd rather just have dmenu autocomplete these for me.

Digging into dmenu

So what really happens when I launch dmenu from i3?

[[email protected] ~]$ cat ~/.config/i3/config | grep mod+d
bindsym $mod+d exec dmenu_run

Alright, what's behind dmenu_run?

[[email protected] ~]$ which dmenu_run 
/usr/bin/dmenu_run

And that's.... A shell script!

[[email protected] ~]$ cat /usr/bin/dmenu_run 
#!/usr/bin/sh
dmenu_path | dmenu "[email protected]" | ${SHELL:-"/bin/sh"} &

I dug into what dmenu_path does, and it just returns a list of commands that dmenu can run (with some fancy caching on top). So all we need to do is whack a couple of extra lines into that command's output.

Let's make this simple. Let's make our own dmenu_run:

#!/usr/bin/bash
cat <(dmenu_path) /dev/stdin <<EOF | dmenu "[email protected]" | ${SHELL:-"/bin/sh"} &
rdp host1
rdp host2
EOF

That lives in my ~/bin/ directory, which exists in my PATH before any of the defaults.

That's it, we're done. dmenu now autocompletes my rdp command.

If you don't know what the <() syntax is, it's bash syntax for process substitution. It spawns the command in the parens, and returns the file handle for stdout of the subprocess as a file handle.