gemini.git

going-flying.com gemini git repository

summary

tree

log

refs

7acd2e8d5e8962bb478ca02092c67729882bcf75 - Matthew Ernisse - 1600095857

new post

view tree

view raw

diff --git a/users/mernisse/articles/14.gmi b/users/mernisse/articles/14.gmi
new file mode 100644
index 0000000..369bd99
--- /dev/null
+++ b/users/mernisse/articles/14.gmi
@@ -0,0 +1,1247 @@
+---
+Title: My .profile, or a decade and a half of workarounds.
+Date: 09/14/2020 10:57
+
+## Preamble
+
+I've been running Linux in one form or another since some wacky
+2.0 kernel version.  I think that was around Slackware 3 something.
+One of the things that has come along with me, ever evolving is
+my .profile.  I don't think I've ever gone through and talked about
+the various parts of it and the pain that caused them.
+
+=> https://ssl.ub3rgeek.net/git/?p=misc.git;a=blob;f=profile;hb=HEAD	My .profile in git
+
+Before we start it is important to remember that this is shell
+agnostic-ish.  I run Debian Linux (bash 5.0), OpenBSD (ksh) and
+macOS (bash 3.2) and this .profile has to run on them all.  That
+said, unlike most of my shell scripts it isn't strictly POSIX
+because it doesn't have to run on dash(1).
+
+## An annotated journey into madness
+
+```
+# mernisse's standard bash/ksh .profile
+#
+# John Crichton: [on a suicide mission] How come I'm not afraid?
+# Ka D'Argo: Fear accompanies the possibility of death. Calm shepherds
+# its certainty.
+# John Crichton: I love hanging with you, man.
+#
+```
+
+Like all good shell scripts why not start with a quote.  I still
+remember this moment in Farscape and it still gives me chills.
+
+```
+set -o vi
+```
+
+Back in like 2001 I was a sysadmin at a fortune 500 and worked on
+big Sun boxes running Solaris 2.6.  I had not yet learned vi and
+one of the other admins snuck this in the global /etc/profile to
+screw with me.  The funny thing is that once I learned it I never
+went back and it is flat out jarring when a shell doesn't respond
+to vi style inputs.
+
+```
+# do this early.  Otherwise bad things might happen
+export HISTFILESIZE=131072
+export HISTSIZE=131072
+export HISTCONTROL=ignoredups
+```
+
+In my last job we had all our admin home directories sitting on a
+NetApp NFS server in our main data center in NY but the servers were
+spread out across North America.  It is amazing what little weirdness
+you could run into with a large .profile, several hundred ms of network
+latency and a large number of shells thanks to screen[1].  We would
+randomly get corrupted history files about once every two weeks until
+we hoisted this nice and early.
+
+=> https://www.gnu.org/software/screen/ [1]
+
+```
+# Set base path, will add more later.
+export PATH=/usr/local/bin:/usr/local/sbin:/bin:/usr/bin:/sbin:/usr/sbin
+
+# Mostly because I like funny sudo(8) and screen(1) messages.
+export NETHACKOPTIONS=gender:male,horsename:Trigger,dogname:Cujo,catname:Fluffy,number_pad,pickup_types:$,color,role:Monk,race:Human,time,showexp,pickup_burden:Unburdened,hilite_pet,DECgraphics
+
+_ostype="$(uname)"
+```
+It has been too long since I played nethack...
+
+```
+# __autoupdater - check the sha256 hash of the local .profile and the one
+# available in the git repository and update if they differ.
+__autoupdater()
+{
+	local _newsum
+	local _shaurl="https://ssl.ub3rgeek.net/profile.sha256"
+	local _profurl="https://ssl.ub3rgeek.net/git/?p=misc.git;a=blob_plain;f=profile;hb=HEAD"
+
+	_newsum="$(fetch_contents $_shaurl)"
+
+	if [ -z "$_newsum" ]; then
+		return
+	fi
+
+	if ! check_hash "$HOME/.profile" "$_newsum"; then
+		echo "Automatically updating .profile"
+		fetch_contents "$_profurl" "$HOME/.profile.new"
+
+		if [ "$?" -gt 0 ]; then
+			echo "failed."
+			echo
+			return
+		fi
+
+		if [ -s "$HOME/.profile.new" ] && "${0#-}" \
+			-n "$HOME/.profile.new"; then
+			mv -f "$HOME/.profile.new" "$HOME/.profile"
+			. "$HOME/.profile"
+		fi
+
+		echo
+	fi
+}
+```
+
+Here we start to get into some fun.  Yep, my dotfiles have an autoupdate
+built right into my .profile.  For the longest time I tried to keep
+everything on NFS so I didn't have to worry about such things but once
+that became essentially impossible (macOS sucks for NFS home directories)
+I decided to implement this.  It also got me to learn git hooks a bit
+since I needed a checksum file available on changes.
+
+```
+# __emit_remote_hostname - Using the Xterm escape codes, set the window
+# title to the hostname of the remote host (determined by looking to see
+# if I am in a screen(1) or tmux(1) session.
+__emit_remote_hostname()
+{
+	if is_tmux_session; then
+		return
+	fi
+	case $TERM in
+		screen*|tmux*)
+			echo -ne "\033k${HOSTNAME%%.*}\033\\"
+			;;
+		*rxvt*|xterm*)
+			echo -ne "\033]0;${HOSTNAME%%.*}\007"
+			;;
+		*)
+			return
+			;;
+	esac
+}
+```
+This used to be a lot more of a thing, when I worked at my last job I
+was managing lots of systems all over the place and knowing which one
+I was on was super important.  This just supports the various escape
+codes for the various terminal emulators I was using.
+
+```
+__get_inetaddr()
+{
+	local _af="inet"
+	local _defgw
+	local _dest="default"
+	local _ipaddr
+
+	if [ -n "$1" ]; then
+		_dest="$1"
+		case  $_dest in
+			[0-9]{,3}\.[0-9]{,3}\.[0-9]{,3}\.[0-9]{,3})
+				: v4 addr
+				;;
+			[a-f0-9]{,4}:*)
+				: v6 addr
+				_af="inet6"
+				;;
+			*)
+				echo "__get_inetaddr() dest arg must be an IP"
+				return 1
+				;;
+		esac
+	fi
+
+	# Viscosity on OS X seems to add a 0/1 route instead of a default
+	# route.  Not 100% sure why this is.  Try to detect it.
+	case $_ostype in
+		Darwin|OpenBSD)
+			if [ "$_dest" = "default" ]; then
+				_defgw="$(route -n get -$_af 0/1 2> /dev/null |\
+					awk '/interface:/ { print $2 }')"
+			fi
+
+			if [ -z "$_defgw" ]; then
+				_defgw="$(route -n get -$_af $_dest \
+					2> /dev/null |\
+					 awk '/interface:/ { print $2 }')"
+			fi
+			;;
+		Linux)
+			if [ "$_dest" = "default" ]; then
+				# Sometimes on newer Linuxes (Debian Jessie
+				# for example) you might have more than one
+				# default route.
+				# So get the Metric and use the lowest one.
+				_defgw="$(route -n -A $_af | awk \
+					'BEGIN {
+						METRIC=65536
+					}
+					/^0\.0\.0\.0/ {
+						if (METRIC > $6) {
+							METRIC=$6
+							IFACE=$8
+						}
+					}
+					END {
+						print IFACE
+					}')"
+			else
+				_defgw="$(ip -f $_af route get $_dest | \
+					awk '{ print $7 }')"
+			fi
+			;;
+	esac
+
+	if [ -n "$_defgw" ]; then
+		# using a shellvar in an awk re, there be quoting
+		# dragons here, boys.
+		_ipaddr="$(ifconfig ${_defgw} | awk \
+			'/'"${_af}"' / { print $2 }')"
+		_ipaddr="${_ipaddr##*:}"
+		if [ -n "$_ipaddr" ]; then
+			echo "$_ipaddr"
+		fi
+	fi
+}
+```
+This grew over years.  I need to know what IP address I am likely using on
+the system so I can setup a back channel.  I don't use this much anymore
+but what it used to do was use this to be able to copy files and open
+applications on whatever machine I was sitting at.  For example if someone
+sent me an e-mail with a pdf in it I had my mail reader (mutt) setup to
+call a wrapper script that would scp the attachment back to wherever I was
+coming from and then use ssh to call another wrapper to open the file in
+the appropriate app (basically a script that selected between xdg-open,
+gnome-open and macOS' open).  I was very proud of this back in the day and
+it worked really well.
+
+```
+# __fixup_hostname - Some shells don't set $HOSTNAME, also add $DOMAINNAME
+# and $SHORTNAME for use elsewhere.
+__fixup_hostname()
+{
+	local __hostname="$(hostname)"
+
+	if [ "$__hostname" = "${__hostname%%.*}" ]; then
+		# Not everyone has hostname -f, but usually
+		# if they don't return FQDN, they do.
+		__hostname="$(hostname -f)"
+	fi
+
+	export SHORTNAME="${__hostname%%.*}"
+	export DOMAINNAME="${__hostname#*.}"
+
+	if [ -z "$HOSTNAME" ]; then
+		export HOSTNAME="$__hostname"
+	fi
+}
+
+#__set_histfile - if we have a NFS homedir, set the histfile
+# to a per-machine-type histfile so we have less random histfile
+# clobbering due to multiple sessions.  This came from lots of pain
+# at Frontier.
+__set_histfile()
+{
+	local __fstype
+
+	# Determine fstype of $HOME -- There needs to be a more portable
+	# way of doing this.
+	case $_ostype in
+		Linux)
+		__fstype="$(stat -fc "%T" $HOME)"
+		;;
+
+	*)
+		return
+		;;
+	esac
+
+	if [ -n "$__fstype" ] && [ "$__fstype" = "nfs" ]; then
+		_shortname="$(echo $SHORTNAME | tr -d [:digit:])"
+		export HISTFILE="$HOME/.$_shortname-history"
+	else
+		export HISTFILE="$HOME/.bash_history"
+	fi
+}
+```
+
+More history file stuff, see the block at the top.  This was to fight the
+same gremlins.  We named our systems [role][##].city.state.domain.tld so
+this meant that if I was connecting to one of the mail servers (say mx12)
+it would store the shell history in a file specific to that server role (mx).
+It also helped with finding that command you ran a week ago in a 100k line
+history.
+
+```
+# add_smartcard - Add the PIV key to the current ssh-agent if available.
+# Requires a opensc compatible smartcard and associated libs and binaries.
+add_smartcard()
+{
+	# Set to a string in the opensc-tool(1) -l output for your card.
+	local _card_name="Yubikey"
+
+	# set to the installed location of the opensc libraries.
+	# on OSX with HomeBrew this is /usr/local/lib
+	local _lib_dir="/usr/local/lib"
+
+	if ! quiet_which opensc-tool; then
+		return
+	fi
+
+	if [ -z "$SSH_AUTH_SOCK" ]; then
+		return
+	fi
+
+	if opensc-tool -l | grep -q "$_card_name"; then
+		if ssh-add -l | grep -q opensc-pkcs11; then
+			return
+		fi
+
+		ssh-add -s "$_lib_dir/opensc-pkcs11.so"
+		return
+
+	fi
+
+	# If card is no longer present, remove the key.
+	if ssh-add -l | grep -q opensc-pkcs11; then
+		ssh-add -e "$_lib_dir/opensc-pkcs11.so" > /dev/null
+	fi
+}
+```
+
+This is some plumbing to let me use my Yubikey as a SSH
+authentication token.  Mostly it exists to work around some
+whackyness with macOS.
+
+```
+# add_to_path - Add directories to $PATH if they exist.
+add_to_path()
+{
+	if [ "$#" -eq 0 ]; then
+		echo "usage add_to_path dir [dir] .. [dir]"
+		return 127
+	fi
+
+	while [ -n "$1" ]; do
+		if [ -d "$1" ]; then
+			export PATH="$PATH:$1"
+		fi
+
+		shift
+	done
+}
+```
+At one point I noticed that my PATH variable was getting filled
+with duplicates and was out of order on some systems.  It turns out
+that I was just doing the usual PATH=$PATH:blahblah all over the
+show and alongside the auto updater which re-sources the .profile
+upon update some real fun was happening.  I was also doing a lot of
+conditional adds based on OS and hostname so instead I decided that
+I'd just always call add_to_path and have it be smart enough to
+not add a directory that doesn't exist.
+
+
+```
+# ssh to a host as the backdoor user.
+backdoor()
+{
+	local remote_host
+	if [ -z "$1" ]; then
+		echo "Usage: backdoor hostname"
+		return 1
+	fi
+
+	remote_host="$1"
+	shift
+
+	if [ ! -f "$HOME/.ssh/backdoor-ssh-key" ]; then
+		echo "Could not find private key file!"
+		return 2
+	fi
+	ssh -i "$HOME/.ssh/backdoor-ssh-key" backdoor@${remote_host} $@
+}
+```
+
+Things you do not do often should be made as easy as possible because
+there is no way you are going to remember.  This is one of those things.
+I use LDAP to store account information and in the rare case that I need
+to get into a system that has lost access to LDAP I have a local user with
+sudo(8) access that I can get into.
+
+```
+# check_hash - Compare a local file to a SHA-2 256 hash.
+check_hash()
+{
+	if [ -z "$1" ] || [ -z "$2" ]; then
+		echo "usage: check_hash file hash"
+		return 127
+	fi
+
+	local _sum
+	local _file="$1"
+	local _hash="$2"
+
+	if [ ! -e "$_file" ]; then
+		echo "check_hash: $_file does not exist or is unreadable."
+		return 2
+	fi
+
+	if quiet_which shasum; then
+		_sum="$(shasum -a 256 $_file | awk '{ print $1 }')"
+
+	elif quiet_which sha256sum; then
+		_sum="$(sha256sum | awk '{ print $1 }')"
+
+	elif quiet_which sha256; then
+		_sum="$(sha256 $_file | awk '{ print $4 }')"
+	else
+		echo "check_hash: could not find suitable checksum program."
+		return 2
+	fi
+
+	if [ "$_hash" = "$_sum" ]; then
+		return 0
+	fi
+
+	return 1
+}
+
+# check_host_alive - ping(1) or ping6(1) a host and determine if it
+# is alive.  This can add up to 2 seconds of delay in execution per
+# call
+check_host_alive()
+{
+	local _pingopts="-q -w 1 -c 1"
+
+	if [ -z "$1" ]; then
+		echo "usage: check_host_alive host"
+		return 127
+	fi
+
+	case $_ostype in
+		Darwin)
+			_pingopts="-q -t 1 -c 1"
+			;;
+	esac
+
+	# try ping(1) before ping6(1)
+	if ping $_pingopts "$1" 2>&1 >/dev/null; then
+		return 0
+	fi
+
+	if ping6 $_pingopts "$1" 2>&1 >/dev/null; then
+		return 0
+	fi
+
+	return 1
+}
+```
+
+These are part of the auto updater.  Always make sure you check both
+IPv4 and IPv6 if you are testing for connectivity *to* a dual-homed
+host.  You never know when you will end up on some poorly configured
+network.
+
+```
+# check_tmux_sessions - Emit information about tmux sessions on a host.
+check_tmux_sessions()
+{
+	if is_tmux_session; then
+		return
+	fi
+
+	if ! quiet_which tmux; then
+		return
+	fi
+
+	local _sessions
+	local _total=0
+	local _attached=0
+
+	_sessions="$(tmux list-sessions 2> /dev/null)"
+
+	if [ -z "$_sessions" ]; then
+		return
+	fi
+
+	_total="$(echo "$_sessions" | wc -l)"
+	_attached="$(echo "$_sessions" | grep -c 'attached')"
+
+	if [ -z "$_attached" ] || [ -z "$_total" ]; then
+		return
+	fi
+
+	if [ "$_attached" -eq "$_total" ]; then
+		return
+	fi
+
+	echo "Found $(( $_total - $_attached )) detached tmux sessions (of $_total)"
+}
+```
+
+I have a bad habit of leaving tmux (used to be screen but I switched to tmux ages ago)
+sessions all over the place.  This lets me know so I can attach to an existing one
+instead of making a new one.
+
+```
+# dotfiles - manage my dotfiles.
+dotfiles()
+{
+	# dotfiles to pull in format src:dst <whitespace>
+	local _dotfiles="
+		ssh_config:.ssh/config
+		tmux.conf:.tmux.conf
+		gitconfig:.gitconfig
+		githelpers:.githelpers
+	"
+
+	local _gitpath="git/?p=dotfiles.git;a=blob_plain;hb=HEAD;f="
+	local _newsum
+	local _url="https://ssl.ub3rgeek.net"
+
+	local fn
+	local src
+
+	for entry in $_dotfiles; do
+		fn="${entry##*:}"
+		src="${entry%%:*}"
+
+		_newsum="$(fetch_contents ${_url}/${src}.sha256)"
+
+		if [ -z "$_newsum" ]; then
+			continue
+		fi
+
+		if ! check_hash "$HOME/$fn" "$_newsum"; then
+			echo "Updating $HOME/$fn"
+
+			fetch_contents "${_url}/${_gitpath}${src}" "$HOME/$fn"
+		fi
+	done
+}
+
+# fetch_contents - Emit the contents of a url to stdout, or to a file.
+fetch_contents()
+{
+	if [ -z "$1" ]; then
+		echo "usage fetch_contents url [file]"
+	fi
+
+	local _fetchcmd
+	local _file="$2"
+	local _ret
+	local _url="$1"
+
+	if [ -n "$_file" ] && [ -e "$_file" ]; then
+		mv -- "$_file" "$_file.old"
+	fi
+
+	if quiet_which curl; then
+		if [ -n "$_file" ]; then
+			curl --fail --silent --location --output "$_file" \
+				"$_url"
+		else
+			curl --fail --silent --location "$_url"
+		fi
+
+	elif quiet_which wget; then
+		if [ -n "$_file" ]; then
+			wget --quiet --output-document="$_file" "$_url"
+		else
+			wget --quiet -O - "$_url"
+		fi
+	else
+		return 2
+	fi
+
+	_ret="$?"
+
+	if [ -n "$_file" ] && [ "$_ret" -ne 0 ] && [ -e "$_file.old" ]; then
+		mv -- "$_file.old" "$_file"
+	fi
+
+	return "$_ret"
+}
+```
+
+More of my auto updater.
+
+```
+# git_branch - Emit branch info for PS1.
+git_branch()
+{
+	if ! quiet_which git; then
+		return
+	fi
+
+	git status > /dev/null 2>&1
+	if [ $? = 128 ]; then
+		return
+	fi
+
+	branch="$(git branch | awk '/^\*/ { print $2 }')"
+	if [ "$branch" = "master" ]; then
+		return
+	fi
+
+	echo -ne "$branch "
+}
+
+# git_clean - Emit colors for PS1 if I am in a gitdir.
+git_clean()
+{
+	local branch=""
+	local clean=""
+
+	if ! quiet_which git; then
+		echo -ne "\033[m"
+		return
+	fi
+
+	git status > /dev/null 2>&1
+	if [ $? = 128 ]; then
+		echo -ne "\033[m"
+		return
+	fi
+
+	git status | grep -qE 'working (directory|tree) clean'
+	if [ $? -gt 0 ]; then
+		clean="\033[1;31m"
+	else
+		clean="\033[1;32m"
+	fi
+	echo -ne "$clean"
+}
+```
+
+These are run as part of my prompt, they check to see if I am in a
+git working copy, and get some information if I am.  More info
+below.
+
+```
+# Test to see if we are in a screen(1) or tmux(1) session.
+is_tmux_session()
+{
+	if [ -n "$STY" ] || [ -n "$TMUX" ]; then
+		return 0
+	fi
+
+	return 1
+}
+
+# is_vm_host - test to see if this is a VM host.
+is_vm_host()
+{
+	case $SHORTNAME in
+		"gypsum"|"tardis"|"virt"*)
+			return 0
+			;;
+	esac
+
+	return 1
+}
+
+
+# lab_prompt - emit color for PS1 if this is a lab system.
+lab_prompt()
+{
+	if [ -f /etc/testing ]; then
+		echo -ne '\033[1;35m'
+	fi
+}
+```
+
+These get used later
+
+```
+# megacli - Wrapper for megacli(1) to suppress logging.
+megacli()
+{
+	if ! quiet_which megacli; then
+		return
+	fi
+	$(which megacli) "$@" -NoLog
+}
+```
+
+Again, if you use something rarely, try to not have to remember.
+In this case megacli more or less loves leaving log files in
+whatever $PWD you were in when you ran it and there is no need
+for that.
+
+```
+# prompt_magic - Emit the pretty dynamic shit on my prompt.
+prompt_magic()
+{
+	if ! is_tmux_session; then
+		echo "$(vol_size)$(vm_count)"
+	fi
+}
+```
+
+Starting to build up the pieces for my prompt.
+
+```
+# quiet_which - Wrapper for which(1) that does not emit anything to stdout.
+quiet_which()
+{
+	if [ -z "$1" ]; then
+		return 1
+	fi
+
+	which "$1" >/dev/null 2>&1
+	return $?
+}
+```
+
+Anything you do a million times should be a function.
+
+```
+# set_display - Use the variable set by ssh_wrapper to manage the
+# dropfile used by my mutt utils for remote display access.
+set_display()
+{
+
+	if [ -n "$X_REMOTE_HOST" ]; then
+		echo "$X_REMOTE_HOST" > ~/.ssh_remote_host_addr
+	else
+		if ! is_tmux_session && \
+		    [ -f ~/.ssh_remote_host_addr ]; then
+			rm -- ~/.ssh_remote_host_addr
+		fi
+	fi
+}
+
+# ssh_wrapper - Wrapper for ssh(1) that tries to set the X_REMOTE_HOST
+# environment variable to the IP address of the local host so that things
+# running on the remote can determine where I am coming from (for other
+# integration with things like X11 and mutt(1).
+ssh_wrapper()
+{
+	local _ipaddr
+	local _ssh_bin
+
+	if ! quiet_which ssh; then
+		return
+	fi
+
+	_ssh_bin="$(which ssh)"
+
+	if [ -z "$X_REMOTE_HOST" ]; then
+		_ipaddr=$(__get_inetaddr)
+
+		if [ -n "$_ipaddr" ]; then
+			export X_REMOTE_HOST="$_ipaddr"
+		fi
+	fi
+
+	if [ -e "$HOME/.ssh/${SHORTNAME}-config" ]; then
+		$_ssh_bin -Y -F "$HOME/.ssh/${SHORTNAME}-config" "$@"
+	else
+		$_ssh_bin "$@"
+	fi
+
+	if is_tmux_session; then
+		tmux set-window-option automatic-rename on >/dev/null
+	fi
+}
+```
+
+These are the big parts of the remote display backchannel stuff.  ssh_wrapper gets
+aliased to ssh later on so it gets called whenever I type ssh <something> so it can
+set the X_REMOTE_HOST environment variable.  It also supports per-host ssh
+configuration files which used to be a lot more important when I had to routinely
+connect to ancient systems.  Once I have connected to a system with ssh_wrapper
+the copy of this .profile on the remote end calls set_display to convert the
+environment variable into a dropfile.  The dropfile is needed so that 1) all shells
+on the system that I am running can see the IP address and 2) the value is updated
+with the latest location.  Imagine I am connected to a system from a work laptop and
+then connect to the same system from home.  I want to have the backchannel stuff
+open up on the system that I'm actually on not the one that I was previously on.
+This way each new login overwrites the value for the previous ones.
+
+```
+# Innanet radio shortcuts
+radio()
+{
+	local _playcmd="mplayer -vo none -playlist"
+
+	case $_ostype in
+		Darwin)
+			if [ -f /Applications/VLC.app/Contents/MacOS/VLC ]; then
+				_playcmd="/Applications/VLC.app/Contents/MacOS/VLC"
+			else
+				_playcmd="open"
+			fi
+			;;
+	esac
+
+	if [ -z "$1" ]; then
+		echo "Usage: radio channel"
+		return 127
+	fi
+
+	case "$1" in
+		# soma.fm
+		"defcon")
+			$_playcmd "http://somafm.com/defcon64.pls"
+			;;
+
+		"metal")
+			$_playcmd "http://somafm.com/metal64.pls"
+			;;
+
+		"trance")
+			$_playcmd "http://somafm.com/thetrip64.pls"
+			;;
+
+		"police")
+			$_playcmd "http://somafm.com/sf103364.pls"
+			;;
+
+		"missionctl")
+			$_playcmd "http://somafm.com/missioncontrol64.pls"
+			;;
+
+		"space")
+			$_playcmd "http://somafm.com/spacestation64.pls"
+			;;
+
+		"doomed")
+			$_playcmd "http://somafm.com/doomed64.pls"
+			;;
+
+		# Mostly Elite: Dangerous stuff...
+		"lave")
+			$_playcmd "http://stream.laveradio.com:8421/stream"
+			;;
+
+		"sidewinder")
+			$_playcmd "http://radiosidewinder.out.airtime.pro:8000/radiosidewinder_b"
+			;;
+
+		"eds")
+			$_playcmd "http://streaming.radionomy.com/EDSRadio"
+			;;
+
+		"orbital")
+			$_playcmd "http://50.7.71.219:7594/"
+			;;
+
+		"bluemars")
+			$_playcmd "http://streams.echoesofbluemars.org:8000/bluemars"
+			;;
+
+		"cryosleep")
+			$_playcmd "http://streams.echoesofbluemars.org:8000/cryosleep"
+			;;
+			
+		"trucker")
+			$_playcmd "http://bluford.torontocast.com:8447/hq"
+			;;
+
+		"galnet")
+			$_playcmd "http://streaming.radionomy.com/galnet"
+			;;
+
+		"list")
+			cat <<-EOM
+			Supported Streams:
+			defcon     - somafm Defcon Radio
+			metal      - somafm Metal
+			trance     - somafm Trance Trip
+			police     - somafm SFPD Feed
+			missonctl  - somafm Mission Control
+			space      - somafm Space Station
+			doomed     - somafm Doomed
+			lave       - Lave Radio
+			sidewinder - Radio Sidewinder
+			eds        - Elite Dangerous Station
+			orbital    - orbital.fm
+			trucker    - Hutton Orbital Radio
+			galnet     - GalNet Radio
+EOM
+			;;
+
+		*)
+			echo "$1 is not supported, add it or try again."
+			;;
+	esac
+}
+```
+
+Pretty simple, open an Internet radio stream.
+
+```
+# vm_capacity, from jwm
+export LIBVIRT_DEFAULT_URI='qemu:///system'
+vm_capacity()
+{
+	if ! quiet_which virsh; then
+		return
+	fi
+
+	_get_system_memory()
+	{
+		sed -n '
+			/^MemTotal:[[:space:]]*/ {
+				s/^MemTotal:[[:space:]]*\([0-9]\{1,\}\)[[:space:]]*[kK][bB][[:space:]]*$/\1/
+				p
+			}
+		' /proc/meminfo
+	}
+
+	capacity_help()
+	{
+		cat - <<-EOF
+		capacity [-ah]
+		  Display information about this hypervisor's memory and disk space
+		  capacity for VMs.
+
+		  -a Display capacity information for all VMs, not just running ones.
+		  -h emit this detailed help information
+		EOF
+	}
+
+	local funcname=capacity
+	local usage="$funcname(): usage: $funcname [-ahm]"
+
+	local all_vms=0
+
+	local old_ifs
+
+	OPTIND=1
+	while getopts :ahm arg; do
+		case $arg in
+		a)
+			all_vms=1
+			;;
+		h)
+			capacity_help
+			return 0
+			;;
+		*)
+			echo 1>&2 "$usage"
+			return 2
+			;;
+		esac
+	done
+	if [ $OPTIND -gt 1 ]; then
+		shift $(( $OPTIND - 1 ))
+	fi
+
+	if [ $# -gt 0 ]; then
+		warn EINVAL "$funcname(): extra arguments: '$@'"
+		echo 1>&2 "$usage"
+		return 2
+	fi
+
+	if [ $all_vms -eq 1 ]; then
+		awk='$3 == "running" || $3 " " $4 == "shut off" {print $2}'
+	else
+		awk='$3 == "running" {print $2}'
+	fi
+
+	allocd_memory=0
+	total_du=0
+	total_stat=0
+	for domain in $(virsh -c qemu:///system list --all | awk "$awk"); do
+		# Skip the header.
+		if [ "$domain" = Name ]; then
+			continue
+		fi
+
+		dom_memory=$(virsh -c qemu:///system dominfo "$domain" |
+			sed -n '
+				/Used memory:/ {
+					s/.*Used memory:[[:space:]]*\([0-9]\{1,\}\)[[:space:]]*KiB[[:space:]]*$/\1/
+					p
+				}
+			')
+		if [ -z "$dom_memory" ]; then
+			echo 1>&2 "$funcname(): Unable to determine amount of allocated memory for VM $domain."
+			return 1
+		fi
+
+		dom_memory=$(($dom_memory / 1024))
+		allocd_memory=$(($allocd_memory + $dom_memory))
+
+		disk_images=$(virsh domblklist "$domain" |
+			sed 1,2d | awk '{print $2}')
+		old_ifs=$IFS
+		IFS='
+'
+
+		for image in $disk_images; do
+			image_du=$(du -k "$image" | awk '{print $1}')
+			if [ -z "$image_du" ]; then
+				echo 1>&2 "$funcname(): Unable to determine du(1) size of disk image $image for VM $domain."
+				return 1
+			fi
+			total_du=$(($total_du + ($image_du / 1024)))
+
+			image_stat=$(stat -c %s "$image")
+			if [ -z "$image_stat" ]; then
+				echo 1>&2 "funcname(): Unable to determine stat(1) size of disk image $image for VM $domain."
+				return 1
+			fi
+			total_stat=$(($total_stat + ($image_stat / 1024)))
+		done
+		IFS=$old_ifs
+	done
+
+	# Add half a gigabyte so we round up. It's better than
+	# piping to bc(1) for floating point math. :-/
+	allocd_memory=$((($allocd_memory + 512) / 1024))g
+
+	total_du=$((total_du / 1024))g
+	total_stat=$((total_stat / 1024 / 1024))g
+
+	if ! phys_mem=$(_get_system_memory); then
+		echo 1>&2 "$funcname(): Unable to determine size of system memory."
+		return 1
+	fi
+	phys_mem=$(($phys_mem / 1024 / 1024))g
+
+	echo "Memory: $allocd_memory of $phys_mem."
+	echo "Disk: $total_du used of $total_stat allocated."
+}
+
+# vm_count - Emit the count of running and shutdown VMs.
+vm_count()
+{
+	if ! is_vm_host; then
+		return
+	fi
+
+	running="$(virsh -c qemu:///system list | grep -c 'running')"
+	stopped="$(virsh -c qemu:///system list --all | grep -c 'shut off')"
+	echo -e "$running/$stopped "
+}
+
+# vmls - List all registered VMs.
+vmls()
+{
+	if ! is_vm_host; then
+		return
+	fi
+
+	virsh -c qemu:///system list --all
+}
+```
+
+Some commands used to manage the kvm based VMs that I have.
+
+```
+# vol_size - Emit the freespace in /vol for PS1
+vol_size()
+{
+	if [ ! "$SHORTNAME" = "apollo" ]; then
+		return
+	fi
+
+	for line in $(df -h /vol/media | awk '{ print $4 }'); do
+		if [ "$line" = "Avail" ]; then
+			continue
+		fi
+		echo -e "$line "
+	done
+}
+```
+
+This gets used in my prompt as you will see.
+
+
+```
+if [ $RANDOM -ge 16384 ]; then
+	if check_host_alive ssl.ub3rgeek.net; then
+		__autoupdater
+		dotfiles
+	fi
+fi
+```
+
+This is where execution really starts.  If I recall correctly $RANDOM is
+a number between 0 and 32768 so this is essentially a coin-flip to run the
+updater.  If I can reach my git repository then we call the two parts of the
+update process.  __autoupdater() updates .profile itself, including reloading
+it and dotfiles manage updating the config files since they do not need to be
+reloaded like .profile does.
+
+```
+__fixup_hostname
+__set_histfile
+```
+
+Again, gotta get that history file stuff handled fast otherwise it is more
+likely that you will end up with a truncated or empty history file.
+
+```
+export DEBFULLNAME="Matthew Ernisse"
+export DEBEMAIL="mernisse@ub3rgeek.net"
+export DEBSIGN_KEYID="4AE6BF32"
+export GIT_AUTHOR_NAME="Matthew Ernisse"
+export GIT_AUTHOR_EMAIL="matt@going-flying.com"
+```
+
+Setup identity information for various tools, git and debian package development
+specifically.
+
+```
+export PS1="\[\033[0m\$(lab_prompt)\]\h\[\033[0m\]@\t \
+\$(prompt_magic)\
+\[\033[0;36m\]\$(git_branch)\
+\[\$(git_clean)\]\W\[\033[m\] >"
+```
+
+My magical prompt.  This could likely be an entire glog entry of its own.
+The amount of tears and blood shed to get this thing to work reasonably
+well most of the time is volumonous.  The big thing to remember is that
+any time you use non-printing characters (the ISO 6429 nee. ANSI color
+codes for example) you need to wrap them in '\[ \]' or else your shell
+will count them when calculating the cursor position.  That means that
+readline support on your shell command line will be broken in very odd
+ways.  Wildly frustrating.  Also be sure you understand escaping here
+when calling functions as part of this.
+
+```
+alias ssh="ssh_wrapper"
+alias git_remote="git remote show origin"
+alias lvirsh="virsh -c qemu:///system"
+```
+
+Shell aliases.  Again, infrequently used commands get their arguments
+automated and finally hook up the ssh_wrapper as described above.
+
+```
+case $TERM in
+	uxrvt*256color)
+		export TERM="xterm-256color"
+		break
+		;;
+	uxrvt*)
+		export TERM="xterm-color"
+		break
+		;;
+esac
+```
+
+Some terms set TERM to things that not all systems understand so reset them.
+This was a much bigger problem when I was using Linux as a desktop OS.
+
+```
+check_tmux_sessions
+#add_smartcard
+set_display
+
+add_to_path "$HOME/.bin" "$HOME/bin" "$HOME/stuff/scripts"
+
+# homebrew puts bash completion things here.
+if [ -d "/usr/local/etc/bash_completion.d" ]; then
+	for file in $(find "/usr/local/etc/bash_completion.d" -type f); do
+		. "$file"
+	done
+	unset file
+fi
+```
+
+Start calling things that I talked about above, and make sure any
+homebrew packages get their completion scripts loaded.
+
+```
+# new OpenBSD uses doas instead of sudo.
+if [ "$_ostype" == "OpenBSD" ] && quiet_which doas; then
+	sudo()
+	{
+		doas $*
+	}
+
+	sudoedit()
+	{
+		doas vi $*
+	}
+fi
+
+if ! quiet_which sudoedit; then
+	sudoedit()
+	{
+		sudo -e $*
+	}
+fi
+```
+
+Muscle memory is golden.  Protect it.
+
+```
+# Silence the zsh garbage.  I have zero desire to change shells.
+if [ "$_ostype" == "Darwin" ]; then
+	export BASH_SILENCE_DEPRECATION_WARNING=1
+fi
+```
+
+Self explanatory.
+
+```
+if ! is_tmux_session; then
+	if quiet_which uptime; then
+		uptime
+	fi
+
+	if quiet_which fortune; then
+		echo
+		fortune -s
+		echo
+	fi
+fi
+```
+
+Only do these if we aren't running inside of tmux or screen.
+
+```
+# profile.d stuff for local customizations.
+if [ -d "$HOME/.profile.d/" ]; then
+	for fragment in $(find "$HOME/.profile.d/" -type f); do
+		. "$fragment"
+	done
+	unset fragment
+fi
+```
+
+At one point this file got close to 100kb in size and most of it was
+crap wrapped in a giant case $HOSTNAME; in esac statement so I instead
+decided to split things out into per-host fragments and source them.
+The downside is that they don't get auto-updated but the upside
+is that it has kept the size down to a reasonable size.
+
+```
+export PROMPT_COMMAND="__emit_remote_hostname"
+```
+
+
+Finally, emit the current hostname just before sending the prompt.  This
+ended up being late because if something earlier bombed then it would crap
+all over the title of my window.
+
+## Did you make it this far?
+
+Hopefully that was useful if not interesting.  It's been a while since
+I dug into this thing and was fun reliving some of the memories.  Sadly
+the git repository only goes back to 2013 when I built the auto updater
+stuff so I don't have commit messages before that but I have to say that
+what I do have seem to get rather. . . colorful.