wip
[distro-setup] / .bashrc
diff --git a/.bashrc b/.bashrc
index b906d2e5b8560b476f646a1012e481d9554d90e3..428cc6df6710d0e6476a5f51c01c38bf5f5fd24d 100644 (file)
--- a/.bashrc
+++ b/.bashrc
@@ -1,3 +1,17 @@
+# Copyright (C) 2016 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # to debug
 #set -x
 # redirect output to log file. this doesn't work. todo figure out why
@@ -25,6 +39,7 @@
 # assume we want ssh commands to source this file if we are sourcing it,
 # and we haven't specified otherwise already
 [[ ! $BASH_LOGIN_SHELL ]] && export BASH_LOGIN_SHELL=true
+#BASH_LOGIN_SHELL=false # temporary override
 
 # first conditions show that we are an ssh command without an interactive shell
 if [[ $SSH_CONNECTION ]] \
@@ -66,7 +81,9 @@ unalias -a
 # use extra globing features.
 shopt -s extglob
 # include .files when globbing, but ignore files name . and ..
-# setting this also sets dotglob
+# setting this also sets dotglob.
+# Note, this doesn't work in bash 4.4 anymore, for paths with
+# more than 1 directory, like a/b/.foo, since * is fixed to not match /
 export GLOBIGNORE=*/.:*/..
 
 # broken with bash_completion package. Saw a bug for this once. Don't anymore.
@@ -96,26 +113,49 @@ shopt -s histappend
 shopt -s checkwinsize
 # attempt to save multiline single commands as single history entries.
 shopt -s cmdhist
+# enable **
 shopt -s globstar
 
 
 # inside emacs fixes
-if [[ $INSIDE_EMACS ]]; then
+if [[ $RLC_INSIDE_EMACS ]]; then
     # EMACS is used by bash on startup, but we don't need it anymore.
     # plus I hit a bug in a makefile which inherited it
     unset EMACS
-    export INSIDE_EMACS
+    export RLC_INSIDE_EMACS
     export PAGER=cat
     export MANPAGER=cat
     # scp completion does not work, but this doesn't fix it. todo, figure this out
     complete -r scp &> /dev/null
     # todo, remote file completion fails, figure out how to turn it off
+    export NODE_DISABLE_COLORS=1
+    # This get's rid of ugly terminal escape chars in node repl
+    # sometime, I'd like to have completion working in emacs shell for node
+    # the offending chars can be found in lib/readline.js,
+    # things that do like:
+    # stream.write('\x1b[' + (x + 1) + 'G');
+    # We can remove them and keep readline, for example by doing this
+    # to start a repl:
+    #!/usr/bin/env nodejs
+    # var readline = require('readline');
+    # readline.cursorTo = function(a,b,c) {};
+    # readline.clearScreenDown = function(a) {};
+    # const repl = require('repl');
+    # var replServer = repl.start('');
+    #
+    # no prompt, or else readline complete seems to be confused, based
+    # on our column being different? node probably needs to send
+    # different kind of escape sequence that is not ugly. Anyways,
+    # completion doesn't work yet even with the ugly prompt, so whatever
+    #
+    export NODE_NO_READLINE=1
+
 fi
 
 
 if [[ $- == *i* ]]; then
     # for readline-complete.el
-    if [[ $INSIDE_EMACS ]]; then
+    if [[ $RLC_INSIDE_EMACS ]]; then
         # all for readline-complete.el
         stty echo
         bind 'set horizontal-scroll-mode on'
@@ -158,8 +198,8 @@ HISTTIMEFORMAT="%I:%M %p %m/%d "
 HISTCONTROL=ignoredups
 # works in addition to HISTCONTROL to do more flexible things
 # it could also do the same things as HISTCONTROL and thus replace it,
-# but meh
-HISTIGNORE='k **'
+# but meh. dunno why, but just " *" does glob expansion, so use [ ] to avoid it.
+HISTIGNORE='k *:[ ]*'
 
 export BC_LINE_LENGTH=0
 
@@ -173,20 +213,19 @@ C_DEFAULT_DIR=/a
 ###################
 ## include files ###
 ###################
-
-for _x in /a/bin/distro-functions/src/* /a/bin/*/*-function; do
+for _x in /a/bin/distro-functions/src/* /a/bin/!(githtml)/*-function?(s); do
     source "$_x"
 done
 unset _x
 # so I can share my bashrc
-for x in /a/bin/bash_unpublished/*; do source $x; done
+for x in /a/bin/bash_unpublished/source-!(.#*); do source $x; done
 source $(dirname $(readlink -f $BASH_SOURCE))/path_add-function
 source /a/bin/log-quiet/logq-function
 path_add /a/exe
 path_add --ifexists --end /a/opt/adt-bundle*/tools /a/opt/adt-bundle*/platform-tools
-# todo, these need to be renamed to be less generic.
-# sync overrode something else useful
-#path_add $HOME/bin/bash-programs-by-ian/utils
+# based on readme.debian. dunno if this will break on other distros.
+_x=/usr/share/wcd/wcd-include.sh
+if [[ -e $_x ]]; then source $_x; fi
 
 
 ###############
@@ -223,19 +262,16 @@ unalias ls ll grep &>/dev/null ||:
 
 
 
-
 #####################
 ###  functions   ####
 #####################
 
 
-mkdir() { command mkdir -p "$@"; }
-
-
-# fast commit all
-ic() {
-    git commit -am "$*"
-}
+..() { c ..; }
+...() { c ../..; }
+....() { c ../../..; }
+.....() { c ../../../..; }
+......() { c ../../../../..; }
 
 
 # file cut copy and paste, like the text buffers :)
@@ -258,37 +294,10 @@ fpst() { # file paste
     cp "$my_f_tempdir"/* "$target"
 }
 
-# history search
-k() { grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history}  | tail -n 40; }
-
-# horizontal row. used to break up output
-hr() { printf "$(tput setaf 5)█$(tput sgr0)%.0s" $(seq $COLUMNS); }
-
-
-# insensitive find
-ifn () {
-    find -L . -iname "*$**" 2>/dev/null
-}
-
-# test existence / exists
-te() {
-    local ret=0
-    for x in "$@"; do
-        [[ -e "$x" || -L "$x" ]] || ret=1
-    done
-    return $ret
-}
 
 # todo, update this
 complete -F _longopt la lower low rlt rld rl lld ts ll dircp ex fcp fct fpst gr
 
-# use sb instead of s is for sudo redirections, eg. sb 'echo "ok fine" > /etc/file'
-sb() {
-    local SUDOD="$PWD"
-    sudo -i bash -c "$@"
-}
-complete -F _root_command s sb
-
 
 _cdiff-prep() {
     # join options which are continued to multiples lines onto one line
@@ -313,9 +322,9 @@ _cdiff-prep() {
 
 _khfix_common() {
     local h=${1##*@}
-    ssh-keygen -R $h
+    ssh-keygen -R $h -f $(readlink -f ~/.ssh/known_hosts)
     local x=$(timeout 0.1 ssh -v $1 |& sed -rn "s/debug1: Connecting to $h \[([^\]*)].*/\1/p");
-    ssh-keygen -R $x
+    ssh-keygen -R $x -f $(readlink -f ~/.ssh/known_hosts)
 }
 khfix() { # known hosts fix
     _khfix_common "$@"
@@ -332,6 +341,18 @@ a() {
 
 ack() { ack-grep "$@"; }
 
+astudio() {
+    # googling android emulator libGL error: failed to load driver: r600
+    # lead to http://stackoverflow.com/a/36625175/14456
+    export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1
+    /a/opt/android-studio/bin/studio.sh "$@" &r;
+}
+
+b() {
+    # backwards
+    c -
+}
+
 bashrcpush () {
     local startdir="$PWD"
     cd ~
@@ -340,13 +361,32 @@ bashrcpush () {
         tar cz bin/semi-private bin/distro-functions/src | ssh $x tar xz
     done
     cd $(mktemp -d)
-    command cp /a/c/repos/bash/!(.git) ~/.gitconfig .
+    command cp /a/c/repos/bash/!(.git|..|.) ~/.gitconfig .
     for x in "$@"; do
         tar cz * | ssh $x tar xz
     done
     cd "$startdir"
 }
 
+bkrun() {
+    # use -p from interactive shell
+    btrbk-run -p "$@"
+}
+
+bfg() { java -jar /a/opt/bfg-1.12.14.jar "$@"; }
+
+btc() {
+    local f=/etc/bitcoin/bitcoin.conf
+    bitcoin-cli -$(s grep rpcuser= $f) -$(s grep rpcpassword= $f) "$@"
+}
+
+if [[ $RLC_INSIDE_EMACS ]]; then
+    c() { wcd -z 50 -o "$@"; }
+else
+    # lets see what the fancy terminal does from time to time
+    c() { wcd -z 50 "$@"; }
+fi
+
 caa() { git commit --amend --no-edit -a; }
 
 calc() { echo "scale=3; $*" | bc -l; }
@@ -383,22 +423,28 @@ cdiff() {
     done < "$unified"
 }
 
-cgpl ()
+cgpl()
 {
-    if [[ $# == 0 ]]; then
+    if (($#)); then
+        cp /a/bin/data/COPYING "$@"
+    else
         cp /a/bin/data/COPYING .
+    fi
+}
+capache()
+{
+    if (($#)); then
+        cp /a/bin/data/LICENSE "$@"
     else
-        cp /a/bin/data/COPYING "$@"
+        cp /a/bin/data/LICENSE .
     fi
 }
-
 chown() {
     # makes it so chown -R symlink affects the symlink and its target.
     if [[ $1 == -R ]]; then
         shift
         command chown -h "$@"
-        command chown "$@"
-        command chown -RH "$@"
+        command chown -R "$@"
     else
         command chown "$@"
     fi
@@ -408,6 +454,11 @@ cim() {
     git commit -m "$*"
 }
 
+cl() {
+    # choose recent directory. cl = cd list
+    c =
+}
+
 d() { builtin bg; }
 complete -A stopped -P '"%' -S '"' d
 
@@ -481,6 +532,10 @@ envload() { # load environment from a previous: export > file
     done < "$file"
 }
 
+f() {
+    # cd forward
+    c +
+}
 
 fa() {
     # find array. make an array of file names found by find into $x
@@ -491,6 +546,11 @@ fa() {
     done < <(find "$@" -print0);
 }
 
+faf() { # find all files
+    find $@ -type f
+}
+
+fastboot() { /a/opt/androidsdk/platform-tools/fastboot "$@"; }
 
 ff() {
     if type -P firefox &>/dev/null; then
@@ -570,6 +630,10 @@ rename-test() {
     return 0
 }
 
+feh() {
+    # F = fullscren, z = random, Z = auto zoom
+    command feh -FzZ "$@"
+}
 
 funce() {
     # like -e for functions. returns on error.
@@ -586,6 +650,30 @@ fw() {
     firefox -P default "$@" >/dev/null 2>&1
 }
 
+getdir () {
+    local help="Usage: getdir [--help] PATH
+Output the directory of PATH, or just PATH if it is a directory."
+    if [[ $1 == --help ]]; then
+        echo "$help"
+        return 0
+    fi
+    if [[ $# -ne 1 ]]; then
+        echo "getdir error: expected 1 argument, got $#"
+        return 1
+    fi
+    if [[ -d $1 ]]; then
+        echo "$1"
+    else
+        local dir="$(dirname "$1")"
+        if [[ -d $dir ]]; then
+            echo "$dir"
+        else
+            echo "getdir error: directory does not exist"
+            return 1
+        fi
+    fi
+}
+
 git_empty_branch() { # start an empty git branch. carefull, it deletes untracked files.
     [[ $# == 1 ]] || { echo 'need a branch name!'; return 1;}
     local gitroot
@@ -596,13 +684,32 @@ git_empty_branch() { # start an empty git branch. carefull, it deletes untracked
     git clean -fdx
 }
 
-gr() {
-    grep -iIP --color=auto "$@"
-}
+gitroot() {
+    local help="Usage: gitroot [--help]
+Print the full path to the root of the current git repo
 
+Handles being within a .git directory, unlike git rev-parse --show-toplevel,
+and works in older versions of git which did not have that."
+    if [[ $1 == --help ]]; then
+        echo "$help"
+        return
+    fi
+    local p=$(git rev-parse --git-dir) || { echo "error: not in a git repo" ; return 1; }
+    [[ $p != /* ]] && p=$PWD
+    echo "${p%%/.git}"
+}
 
+# quit will prompt if the program crashes.
+gmacs() { gdb -ex=r -ex=quit --args emacs "$@"; r; }
 
+gse() {
+    git send-email --notes '--envelope-sender=<ian@iankelling.org>' \
+        --suppress-cc=self "$@"
+}
 
+gr() {
+    grep -iIP --color=auto "$@"
+}
 
 grr() {
     if [[ ${#@} == 1 ]]; then
@@ -612,6 +719,17 @@ grr() {
     fi
 }
 
+hstatus() {
+    # do git status on published repos
+    cd /a/bin/githtml
+    for x in !(forks) forks/* ian-specific/*; do
+        cd `readlink -f $x`/..
+        hr
+        echo $x
+        i status
+        cd /a/bin/githtml
+    done
+}
 
 hl() { # history limit. Write extra history to archive file.
     # todo: this is not working or not used currently
@@ -632,10 +750,18 @@ hl() { # history limit. Write extra history to archive file.
     if (($linecount > $max_lines)); then
         prune_lines=$(($linecount - $max_lines))
         head -n $prune_lines "$HISTFILE" >> "$harchive" \
-            && sed -ie "1,${prune_lines}d"  $HISTFILE
+            && sed --follow-symlinks -ie "1,${prune_lines}d"  $HISTFILE
     fi
 }
 
+hr() { # horizontal row. used to break up output
+    printf "$(tput setaf 5)█$(tput sgr0)%.0s" $(seq $COLUMNS)
+    echo
+}
+
+hrcat() { local f; for f; do [[ -f $f ]] || continue; hr; echo "$f"; cat "$f"; done }
+
+
 i() { git "$@"; }
 # modified from ~/local/bin/git-completion.bash
 # other completion commands are mostly taken from bash_completion package
@@ -649,7 +775,20 @@ if ! type service &>/dev/null; then
     }
 fi
 
+ic() {
+    # fast commit all
+    git commit -am "$*"
+}
+
+idea() {
+    /a/opt/idea-IC-163.7743.44/bin/idea.sh "$@" &r
+}
 
+ifn() {
+    # insensitive find
+    find -L . -not \( -name .svn -prune -o -name .git -prune \
+         -o -name .hg -prune \) -iname "*$**" 2>/dev/null
+}
 
 
 if [[ $OS == Windows_NT ]]; then
@@ -683,6 +822,10 @@ istext() {
     grep -Il "" "$@" &>/dev/null
 }
 
+jtail() {
+    journalctl -n 10000 -f "$@" | grep -Evi "^(\S+\s+){4}(sudo|sshd|cron)"
+}
+
 
 l() {
     if [[ $PWD == /[iap] ]]; then
@@ -718,23 +861,30 @@ lower() { # make first letter of filenames lowercase.
     done
 }
 
-make-targets() {
-    # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
-    make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
+
+k() { # history search
+    grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history}  | tail -n 40;
 }
 
 
-md5diff() {
-    [[ $(md5sum < "$1") != $(md5sum < "$2") ]]
+make-targets() {
+    # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
+    make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
 }
 
 
-
 mkc() {
     mkdir "$1"
     c "$1"
 }
 
+mkdir() { command mkdir -p "$@"; }
+
+pithos() {
+    cd /
+    export PYTHONPATH=/a/opt/Pithosfly
+    python3 -m pithos&r
+}
 
 pakaraoke() {
     # from http://askubuntu.com/questions/456021/remove-vocals-from-mp3-and-get-only-instrumentals
@@ -784,8 +934,15 @@ pub() {
     rld /a/h/_site/ li:/var/www/iankelling.org/html
 }
 
+pubip() { curl -4s https://icanhazip.com; }
+whatismyip() { pubip; }
+
+
 pwgen() {
-    apg -s -m 10 -x 14 -t
+    # -m = min length
+    # -x = max length
+    # -t = print pronunciation
+    apg -m 12 -x 16 -t
 }
 
 
@@ -820,12 +977,13 @@ rlt() {
 }
 
 rlu() { # [OPTS] HOST PATH
-    # eg rlu -opts frodo testpath
+    # eg rlu -opts frodo /testpath
     # useful for selectively sending dirs which have been synced with unison,
     # where the path is the same on both hosts.
     opts=("${@:1:$#-2}") #  1 to last -2
     path="${@:$#}" # last
     host="${@:$#-1:1}" # last -1
+    if [[ $path == .* ]]; then echo error: need absolut path; return 1; fi
     # rync here uses checksum instead of time so we don't mess with
     # unison relying on time as much. g is for group, same reason
     # to keep up with unison.
@@ -833,7 +991,8 @@ rlu() { # [OPTS] HOST PATH
 }
 
 
-rspicy() { # HOST DOMAIN
+rspicy() { # usage: HOST DOMAIN
+    # connect to spice vm remote host. use vspicy for local host
     local port=$(ssh $1<<EOF
 sudo virsh dumpxml $2|grep "<graphics.*type='spice'" | \
        sed -rn "s/.*port='([0-9]+).*/\1/p"
@@ -848,12 +1007,8 @@ EOF
 
 s() {
     # background
-    # alias s='SUDOD="$PWD" sudo -i '
-    # because this is an alias, and the extra space at the end, it would allow
-    #  aliases to be used with it. but aliases aren't used in scripts,
-    #  better to eliminate inconsistencies. Plus, you can't do s=s; $s command
-    #  with an alias, which I like to do in some functions
-    # extra space at the end allows aliases to work
+    # I use a function because otherwise we can't use in a script,
+    # can't assign to variable.
     #
     # note: gksudo is recommended for X apps because it does not set the
     # home directory to the same, and thus apps writing to ~ fuck things up
@@ -879,8 +1034,21 @@ safe_rename() {
     fi
 }
 
-sdf() {
-    c /sdx/test/sandbox/
+
+sb() { # sudo bash -c
+    # use sb instead of s is for sudo redirections,
+    # eg. sb 'echo "ok fine" > /etc/file'
+    local SUDOD="$PWD"
+    sudo -i bash -c "$@"
+}
+complete -F _root_command s sb
+
+scssl() {
+    # s gem install scss-lint
+    pushd /a/opt/thoughtbot-guides
+    git pull --stat
+    popd
+    scss-lint -c /a/opt/thoughtbot-guides/style/sass/.scss-lint.yml "$@"
 }
 
 ser() {
@@ -950,12 +1118,29 @@ srm () {
     command srm -ll "$@"
 }
 
+srun() {
+    scp $2 $1:/tmp
+    ssh $1 /tmp/${2##*/} "${@:2}"
+}
+
+swap() {
+    local tmp
+    tmp=$(mktemp)
+    mv $1 $tmp
+    mv $2 $1
+    mv $tmp $2
+}
+
 t() {
     local x
     local -a args
     if type -t trash-put >/dev/null; then
-        # skip args that don't exist, or else it's an err
-        for x in "$@"; do [[ ! -e $x ]] || args+=("$x"); done
+        # skip args that don't exist, or else trash-put will have an error
+        for x in "$@"; do
+            if [[ -e $x || -L $x ]]; then
+                args+=("$x")
+            fi
+        done
         [[ ! ${args[@]} ]] || trash-put "${args[@]}"
     else
         rm -rf "$@"
@@ -998,9 +1183,23 @@ tclock() {
 }
 
 
+te() {
+    # test existence / exists
+    local ret=0
+    for x in "$@"; do
+        [[ -e "$x" || -L "$x" ]] || ret=1
+    done
+    return $ret
+}
+
+testmail() {
+    declare -gi _seq; _seq+=1
+    echo "test body" | m mail -s "test mail from $HOSTNAME, $_seq" "${1:-root@localhost}"
+}
+
 tm() {
     # timer in minutes
-    (sleep $(calc "$@ * 60") && mpv --volume 50 /a/bin/data/alarm.mp3) > /dev/null 2>&1 &
+    (sleep $(calc "$@ * 60") && mpv --volume 50 /a/bin/data/alarm.mp3 --loop=no) > /dev/null 2>&1 &
 }
 
 ts() { # start editing a new file
@@ -1055,6 +1254,145 @@ tx() { # toggle set -x, and the prompt so it doesn't spam
     fi
 }
 
+psvpn() {
+    # show all processes in the vpn network namespace.
+    # blank entries appear to be subprocesses/threads of transmission daemon
+    ps -w | head -n 1
+    s find -L /proc/[1-9]*/task/*/ns/net -samefile /run/netns/vpn | cut -d/ -f5 | \
+        while read l; do
+            x=$(ps -w --no-headers -p $l);
+            if [[ $x ]]; then echo "$x"; else echo $l; fi;
+        done
+}
+
+m() { printf "%s\n" "$*";  "$@"; }
+
+vpnbash() {
+    m s nsenter -t $(pgrep openvpn) -n -m bash
+}
+
+netnsvpn() {
+    # todo, make a function to kill all processes in the network namespace.
+
+    # manually run vpn so it stays within a network namespace,
+    # until I get it all wired up with systemd.
+    if ! s ip netns list | awk '{print $1}' | grep -Fx vpn &>/dev/null; then
+        newns vpn start || return 1
+    fi
+
+    s iptables-restore <<'EOF'
+# some traffic leaked, so I recreated the rules here being
+# a little more specific. We could also do the reverse rules
+# for input, but meh.
+# todo: try out rules for process owner. reject all
+# packes by transmission-daemon, which are not from brvpn
+*filter
+:INPUT ACCEPT
+:FORWARD ACCEPT
+:OUTPUT ACCEPT
+# -i = interface
+# -d = destination
+# -p = protocol
+# -m = (match, aka extended match module), enabling the next rule
+-A FORWARD -i brvpn -d 192.168.1.1 -p udp -m udp --dport 53 -j ACCEPT
+-A FORWARD -i brvpn -d 192.168.1.1 -p tcp -m tcp --dport 53 -j ACCEPT
+-A FORWARD -i brvpn -d 192.168.1.0/24 -p tcp -m tcp --dport 9091 -j ACCEPT
+-A FORWARD -i brvpn -p udp -m udp --dport 1194:1195 -j ACCEPT
+-A FORWARD -i brvpn -j REJECT
+# prevent transmission daemon from doing anything outside it's
+# network namespace.
+-A OUTPUT -m owner --uid-owner debian-transmission -j REJECT
+COMMIT
+EOF
+    local pid
+    pid=$(< /run/openvpn/client.pid)
+    local vpn_on=false
+    if [[ $pid ]]; then
+        if [[ -e /proc/$pid ]]; then
+            vpn_on=true
+        else
+            vpn_on=false
+            s rm -f /run/openvpn/client.pid
+        fi
+    fi
+    # for testing of disabled firewall rules, run this:
+    #s ip netns exec vpn iptables -P OUTPUT ACCEPT
+    s ip netns exec vpn iptables-restore <<'EOF'
+# format from iptables-save. [0:0] are comments of packet-count/byte-count
+# which I removed
+*filter
+:INPUT DROP
+:FORWARD ACCEPT
+:OUTPUT DROP
+# from ip route, we can deduce that traffic goes to the
+# local 10.8.0.x tun0, then to the normal interface.
+# For the normal interface, we allow only some ports:
+# dns, vpn, transmission-remote.
+# dns is only used to resolve the vpn server ip on initial
+# connection.
+# rules are mirror on input and output, just for extra safety,
+# although just having output should do fine.
+
+# We could also firewall from outside the nat, for example like this,
+# but I'm thinking this is simpler.
+#-A FORWARD -i brvpn -p udp -m udp --dport 1194:1195 -j ACCEPT
+#-A FORWARD -i brvpn -j REJECT
+
+# help prevent dns leaks, openvpn runs as root
+-A OUTPUT -p udp -m udp --dport 53 -m owner --uid-owner root -j ACCEPT
+-A INPUT -p udp -m udp --dport 53 -m owner --uid-owner root -j ACCEPT
+
+-A OUTPUT -p tcp -m tcp --dport 53 -m owner --uid-owner root -j ACCEPT
+-A INPUT -p tcp -m tcp --dport 53 -m owner --uid-owner root -j ACCEPT
+
+-A OUTPUT -p tcp -m tcp --sport 9091 -j ACCEPT
+-A INPUT -p tcp -m tcp --dport 9091 -j ACCEPT
+
+# 1195 is used for the secondary vpn server
+-A OUTPUT -p udp -m udp --dport 1194:1195 -j ACCEPT
+-A INPUT -p udp -m udp --dport 1194:1195 -j ACCEPT
+
+-A OUTPUT -o tun0 -j ACCEPT
+-A INPUT -i tun0 -j ACCEPT
+COMMIT
+EOF
+    $vpn_on || s ip netns exec vpn /usr/sbin/openvpn --daemon ovpn --status /run/openvpn/%i.status 10 --cd /etc/openvpn --config /etc/openvpn/client.conf --writepid /run/openvpn/client.pid
+}
+
+
+
+vc() {
+    [[ $1 ]] || { e "$0: error, expected cmd to run"; return 1; }
+    gksudo -- ip netns exec vpn gksudo -u ${SUDO_USER:-$USER} "$@"
+}
+
+transmission-stop() {
+    local pid=$(cat /var/lib/transmission-daemon/transmission-daemon.pid)
+    if [[ $pid ]]; then
+        sudo kill $pid
+    else
+        psg transmission-daemon
+    fi
+}
+
+
+trg() { transmission-remote-gtk&r; }
+
+# transmission() {
+#     local pid=$(cat /var/lib/transmission-daemon/transmission-daemon.pid)
+#     if [[ $pid && -e /proc/$pid ]]; then
+#         echo "noop. already running."
+#         return
+#     fi
+
+#     local NAME=transmission-daemon
+#     local DAEMON=/usr/bin/$NAME
+#     local duser=debian-transmission
+
+#     [ -e /etc/default/$NAME ] && . /etc/default/$NAME
+#     s ip netns exec vpn sudo -u $duser ionice -c 3 nice -n 19 $DAEMON $OPTIONS
+# }
+
 virshrm() {
     for x in "$@"; do virsh destroy "$x"; virsh undefine "$x"; done
 }
@@ -1084,6 +1422,7 @@ vpn() {
     journalctl --unit=openvpn@client -f -n0
 }
 
+
 vpnoff() {
     s systemctl stop openvpn@client
 }
@@ -1096,7 +1435,7 @@ vrm() {
 
 
 
-vspicy() {
+vspicy() { # usage: VIRSH_DOMAIN
     # connect to vms made with virt-install
     spicy -p $(sudo virsh dumpxml "$1"|grep "<graphics.*type='spice'"|\
                    sed -r "s/.*port='([0-9]+).*/\1/")
@@ -1112,7 +1451,7 @@ whatismyip() { curl ipecho.net/plain ; echo; }
 
 if [[ $- == *i* ]]; then
     # commands to run when bash exits normally
-    trap "hl; _smh" EXIT
+    trap "hl" EXIT
 fi
 
 
@@ -1210,7 +1549,7 @@ if [[ $- == *i* ]]; then
     shopt -s autocd
     shopt -s dirspell
     PS1='\w'
-    if [[ $- == *i* ]]  && [[ ! $INSIDE_EMACS ]]; then
+    if [[ $- == *i* ]]  && [[ ! $RLC_INSIDE_EMACS ]]; then
         PROMPT_DIRTRIM=2
        bind -m vi-command B:shell-backward-word
        bind -m vi-command W:shell-forward-word
@@ -1222,10 +1561,11 @@ if [[ $- == *i* ]]; then
 
     prompt_command() {
         local return=$? # this MUST COME FIRST
-        local psc pst
-        local ps_char ps_color
+        local psc pst ps_char ps_color stale_subvol
         unset IFS
         history -a # save history
+
+        # for titlebar
         if [[ ! $DESKTOP_SESSION == xmonad && $TERM == *(screen*|xterm*|rxvt*) ]]; then
             # from the screen man page
             if [[ $TERM == screen* ]]; then
@@ -1236,6 +1576,7 @@ if [[ $- == *i* ]]; then
            echo -ne "$title_escape${PWD/#$HOME/~}  $USER@$HOSTNAME\007"
        fi
 
+
         case $return in
            0) ps_color="$(get_term_color blue)"
                ps_char='\$'
@@ -1254,6 +1595,11 @@ if [[ $- == *i* ]]; then
                ps_color="$(get_term_color bold green)"
             fi
         fi
+        # I would set nullglob, but bash has had bugs where that
+        # doesn't work if not in top level.
+        if [[ -e /nocow/btrfs-stale ]] && ((`ls -AUq /nocow/btrfs-stale|wc -l`)); then
+            ps_char="! $ps_char"
+        fi
         PS1="${PS1%"${PS1#*[wW]}"} \[$ps_color\]$ps_char\[$(get_term_color nocolor)\] "
         # emacs completion doesn't like the git prompt atm, so disabling it.
         #PS1="${PS1%"${PS1#*[wW]}"}$(__git_ps1 ' (%s)') \[$ps_color\]$ps_char\[$(get_term_color nocolor)\] "
@@ -1263,8 +1609,6 @@ fi
 
 
 
-
-
 ###########################################
 # stuff that makes sense to be at the end #
 ###########################################
@@ -1301,6 +1645,9 @@ fi
 # based on warning from rvmsudo
 export rvmsudo_secure_path=1
 
+# for other script I wrote
+#export ACME_TINY_PATH=/a/opt/acme-tiny
+export ACME_TINY_WRAPPER_CERT_DIR=/p/c/machine_specific/$HOSTNAME/webservercerts
 
 if [[ -s "/usr/local/rvm/scripts/rvm" ]]; then
     source "/usr/local/rvm/scripts/rvm"
@@ -1308,11 +1655,25 @@ elif [[ -s $HOME/.rvm/scripts/rvm ]]; then
     source $HOME/.rvm/scripts/rvm
 fi
 
+
+path_add --end ~/.npm-global
+
+
+# didn't get drush working, if I did, this seems like the
+# only good thing to include for it.
+# Include Drush completion.
+# if [ -f "/home/ian/.drush/drush.complete.sh" ] ; then
+#     source /home/ian/.drush/drush.complete.sh
+# fi
+
+
 # https://wiki.archlinux.org/index.php/Xinitrc#Autostart_X_at_login
 # i added an extra condition as gentoo xorg guide says depending on
 # $DISPLAY is fragile.
 if [[ ! $DISPLAY && $XDG_VTNR == 1 ]] && shopt -q login_shell && isarch; then
     exec startx
 fi
+
+
 # ensure no bad programs appending to this file will have an affect
 return 0