various fixes
[distro-setup] / brc2
1 #!/bin/bash
2 # Copyright (C) 2019 Ian Kelling
3 # SPDX-License-Identifier: AGPL-3.0-or-later
4 # this gets sourced. shebang is just for file mode detection
5
6
7 # * settings
8
9 HISTFILE=$HOME/.bh
10
11 source /a/bin/distro-setup/path-add-function
12 path-add /a/exe
13 # add this with absolute paths as needed for better security
14 #path-add --end /path/to/node_modules/.bin
15 ## for yarn, etc
16 #path-add --end /usr/lib/node_modules/corepack/shims/
17
18 # pip3 --user things go here:
19 path-add --end ~/.local/bin
20 path-add --ifexists --end /a/work/libremanage
21 path-add --ifexists --end /a/opt/adt-bundle*/tools /a/opt/adt-bundle*/platform-tools
22 path-add --ifexists --end /a/opt/scancode-toolkit-3.10.
23
24
25 export WCDHOME=/a
26
27
28 case $EUID in
29 0)
30 SL_SSH_ARGS="-F $HOME/.ssh/confighome"
31 ;;
32 esac
33
34
35 # * include files
36
37 # generated instead of dynamic for the benefit of shellcheck
38 #for x in /a/bin/distro-functions/src/* /a/bin/!(githtml)/*-function?(s); do echo source $x ; done
39 source /a/bin/distro-functions/src/identify-distros
40 source /a/bin/log-quiet/logq-function
41 # for x in /a/bin/bash_unpublished/source-!(.#*); do echo source $x; done
42 source /a/bin/bash_unpublished/source-semi-priv
43 source /a/bin/bash_unpublished/source-state
44
45 source /a/bin/log-quiet/logq-function
46 if [[ -s /a/opt/alacritty/extra/completions/alacritty.bash ]]; then
47 source /a/opt/alacritty/extra/completions/alacritty.bash
48 fi
49
50
51 # * functions
52
53 hstest() {
54 install-my-scripts
55 d=$(mktemp -d)
56 sed '/^ *IdentityFile/d' ~/.ssh/config >$d/config
57 s command ssh -F $d/config -i /q/root/h "$@"
58 }
59
60 hrtest() {
61 install-my-scripts
62 d=$(mktemp -d)
63 sed '/^ *IdentityFile/d' ~/.ssh/config >$d/config
64 s rsync -e "ssh -F $d/config -i /q/root/h" "$@"
65 }
66
67
68 slemacs() {
69 local arg rtime v
70 arg="$1"
71 remote="$2"
72 if [[ $arg == [89]0Etiona* ]]; then
73 v=${arg::1}
74 rtime=${arg#*Etiona} # remote time
75 if [[ ! $rtime ]]; then
76 rtime=0
77 fi
78 dir=/a/opt/emacs-trisquel${v}-nox/.iank
79 ltime=$(stat -c%Y $dir/e/e/.emacs.d/init.el)
80 if (( ltime > rtime )); then
81 m rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" $dir "$remote":/home/iank
82 fi
83 fi
84 }
85
86 sle() { # sl emacs
87 local f=/home/iank/.emacs.d/init.el
88 sl --sl-test-cmd ". /etc/os-release ; printf %s \${VERSION//[^a-zA-Z0-9]/}; test -e $f && stat -c%Y $f" --sl-test-hook slemacs "$@"
89 }
90 ccomp ssh sle
91
92 # Run this manually after .emacs.d changes. Otherwise, to check if
93 # files changed with find takes 90ms. sl normally only adds 25ms. We
94 # could cut it down to 10ms if we put things on a btrfs filesystem and
95 # looked for changes there, or used some inotify thing, but that seems
96 # like too much work.
97 egh() { # emacs gnuhope
98 RSYNC_RSH=ssh m rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" /a/opt/emacs-trisquel9-nox/.iank lists2d.fsf.org:.ianktrisquel_9
99 RSYNC_RSH=ssh m rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" /a/opt/emacs-trisquel8-nox/.iank lists2d.fsf.org:/home/iank
100 }
101 ekw() {
102 local shell="bash -s"
103 if [[ $HOSTNAME != kw ]]; then
104 shell="ssh kw.office.fsf.org"
105 bbk -m /a -t kw
106 fi
107 $shell <<'EOF'
108 sudo mkdir /root/.ianktrisquel_9
109 sudo rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" /a/opt/emacs-trisquel9-nox/.iank /root/.ianktrisquel_9
110 rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" /a/opt/emacs-trisquel8-nox/.iank /home/iank
111 EOF
112 }
113
114 # usage mkschroot [-] distro codename packages
115 # - means no piping in of sources.list
116 mkschroot() {
117 local force=false
118 while [[ $1 == -* ]]; do
119 case $1 in
120 -f) force=true; shift ;;
121 -s)
122 sources="$2"
123 if [[ ! -s $sources ]]; then
124 echo mkschroot: error: sources file $sources does not exist or is empty
125 return 1
126 fi
127 shift 2
128 ;;
129 esac
130 done
131 distro=$1
132 shift
133 case $distro in
134 trisquel)
135 repo=http://mirror.fsf.org/trisquel/
136 ;;
137 ubuntu)
138 repo=http://archive.ubuntu.com/ubuntu/
139 ;;
140 debian)
141 repo=http://deb.debian.org/debian/
142 ;;
143 esac
144 n=$1
145
146 shift
147 if ! $force && schroot -l | grep -xFq chroot:$n; then
148 echo "$0: $n schroot already installed, skipping"
149 return 0
150 fi
151 apps=($@)
152 d=/nocow/schroot/$n
153 sd /etc/schroot/chroot.d/$n.conf <<EOF
154 [$n]
155 description=$n
156 type=directory
157 directory=$d
158 profile=desktop
159 preserve-environment=true
160 users=$USER,user2
161 EOF
162 cd
163 if [[ ! -e $d/bin ]]; then
164 sudo mkdir -p $d
165 # resolvconf otherwise schroot fails with
166 # cp: not writing through dangling symlink '/var/run/schroot/mount/flidas-7a2362e0-81b3-4848-92c1-610203ef5976/etc/resolv.conf'
167 sudo debootstrap --exclude=resolvconf $n $d $repo
168 fi
169 if [[ $sources ]]; then
170 sudo install -m 644 $sources $d/etc/apt/sources.list
171 fi
172 sudo chroot $d apt-get update
173 sudo DEBIAN_FRONTEND=noninteractive chroot $d apt-get -y dist-upgrade --purge --auto-remove
174 sudo cp -P {,$d}/etc/localtime
175 if (( ${#apps[@]} )); then
176 sudo DEBIAN_FRONTEND=noninteractive schroot -c $n -- apt-get install --allow-unauthenticated -y ${apps[@]}
177 fi
178 }
179
180
181 # note: this is incomplete and untested.
182 # https://wiki.archlinux.org/index.php/Install_Arch_Linux_from_existing_Linux#Creating_a_chroot
183 mkarchchroot() {
184 local tarball mirror
185 mirror=https://mirrors.edge.kernel.org/archlinux/iso/latest/
186 tarball=$(curl -s $mirror | sed -nr 's/.*"(archlinux-bootstrap-.*-x86_64.tar.gz)".*/\1/p')
187 wget -O /tmp/arch.tar.gz https://mirrors.edge.kernel.org/archlinux/iso/latest/$tarball
188 s mkdir -p /nocow/schroot/arch
189 cd _/nocow/schroot/arch
190 s sed -i '/## United States/,/^$/s,^#,,' etc/pacman.d/mirrorlist
191 # error: could not determine cachedir mount point /var/cache/pacman/pkg
192 s sed -i /^CheckSpace/d etc/pacman.conf
193 chroot . /bin/bash -s <<'EOF'
194 pacman-key --init
195 pacman-key --populate archlinux
196 pacman -Syyu
197 EOF
198 # example of building an aur package:
199 # pacman -Sy base-devel wget
200 # useradd -m iank
201 # f=$target/etc/sudoers
202 # line='iank ALL=(ALL) NOPASSWD: ALL'
203 # if [[ ! -e $f ]] || ! grep -xF "$line" $f; then
204 # echo "$line" >> $f
205 # fi
206 # su iank
207 # wget https://aur.archlinux.org/cgit/aur.git/snapshot/anbox-image-gapps.tar.gz
208 # tar xzf anbox-image-gapps.tar.gz
209 # cd anbox-image-gapps
210 # makepkg -s
211 }
212
213
214 # clock back in to timetrack from last entry
215 tback() {
216 sqlite3 /p/.timetrap.db "update entries set end = NULL where id = (select max(id) from entries);"
217 }
218
219 # sshfs example:
220 # s sshfs bu@$host:/bu/home/md /bu/mnt -o reconnect,ServerAliveInterval=20,ServerAliveCountMax=30 -o allow_other
221
222 eqgo() {
223 enn -M $(exiqgrep -i)
224 }
225 eqgo1() {
226 enn -M $(exiqgrep -i|h1)
227 }
228
229
230 gnupload(){
231 /a/f/gnulib/build-aux/gnupload "$@"
232 }
233
234 abrowserrmcompat() {
235 local f
236 ngset
237 f=(/p/c/firefox*/compatibility.ini)
238 if (( ${#f[@]} )); then
239 rm ${f[@]}
240 fi
241 ngreset
242 }
243 ngset() {
244 if shopt nullglob >/dev/null; then
245 ngreset=false
246 else
247 shopt -s nullglob
248 ngreset=true
249 fi
250 }
251 ngreset() {
252 if $ngreset; then
253 shopt -u nullglob
254 fi
255 }
256
257 checkre() {
258 s checkrestart -b /a/bin/ds/checkrestart-blacklist -pv
259 }
260
261 cp-blocked-domains-to-brains() {
262 cp /a/f/ans/roles/exim/files/mx/simple/etc/exim4/bad-sender_domains /a/f/brains/sysadmin/kb/blocked_email_domains.mdwn
263 }
264 cp-blocked-domains-to-ansible() {
265 cp /a/f/brains/sysadmin/kb/blocked_email_domains.mdwn /a/f/ans/roles/exim/files/mx/simple/etc/exim4/bad-sender_domains
266 }
267
268
269 anki() {
270 # crashes on adding new cards in t9
271 schroot -c buster -- anki
272 }
273
274 acat() {
275 ngset
276 hrcat /m/md/alerts/{cur,new}/*
277 ngreset
278 hr; echo bk; hr
279 ssh bk.b8.nz "shopt -s nullglob; hrcat /m/md/INBOX/new/* /m/md/INBOX/cur/*"
280 }
281 aclear() {
282 ngset
283 rm -f /m/md/alerts/{cur,new}/*
284 ngreset
285 ssh bk.b8.nz "shopt -s nullglob; rm -f /m/md/INBOX/new/* /m/md/INBOX/cur/*"
286 system-status _
287 }
288
289 alerts() {
290 find /var/local/cron-errors /home/iank/cron-errors /sysd-mail-once-state -type f
291 }
292 ralerts() { # remote alerts
293 local ret shell
294 # this list is duplicated in check-remote-mailqs
295 for h in bk je li frodo kwwg x3wg x2wg kdwg sywg; do
296 echo $h:
297 shell="ssh $h"
298 if [[ $HOSTNAME == "${h%wg}" ]]; then
299 shell=
300 fi
301 ret=0
302 $shell find /var/local/cron-errors /home/iank/cron-errors /sysd-mail-once-state -type f || ret=$?
303 if (( ret )); then
304 echo ret:$ret
305 fi
306 done
307 }
308
309 ap() {
310 # pushd in case current directory has an ansible.cfg file
311 pushd /a/xans >/dev/null
312 ansible-playbook -v -l ${1:- $(hostname -f)} site.yml
313 popd >/dev/null
314 }
315 aw() {
316 pushd /a/work/ans >/dev/null
317 time ansible-playbook -v -i inventory adhoc.yml "$@"
318 popd >/dev/null
319 }
320 ad() {
321 pushd /a/bin/distro-setup/a >/dev/null
322 ansible-playbook site.yml "$@"
323 popd >/dev/null
324 }
325
326 astudio() {
327 # googling android emulator libGL error: failed to load driver: r600
328 # lead to http://stackoverflow.com/a/36625175/14456
329 export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1
330 /a/opt/android-studio/bin/studio.sh "$@" &r;
331 }
332
333 # note, to check for glue records
334 # First, find some the .org nameservers:
335 # dig +trace iankelling.org
336 # then, query one:
337 # dig ns1.iankelling.org @b0.org.afilias-nst.org.
338
339 # Now, compare for a domain that does have glue records setup (note the A
340 # and AAAA records in ADDITIONAL SECTION, those are glue records like the
341 # one I'm asking for):
342
343 # $ dig ns1.gnu.org @b0.org.afilias-nst.org.
344
345 # todo: make sm pull/push use systemd instead of the journal cat command
346 bbk() { # btrbk wrapper
347 local ret=0
348 c /
349 local active=true
350 systemctl is-active btrbk.timer || active=false
351 if $active; then
352 ser stop btrbk.timer
353 fi
354 if [[ $(systemctl is-active btrbk.service ||:) != inactive ]]; then
355 echo "cron btrbk is already running"
356 if $active; then ser start btrbk.timer; fi
357 return 1
358 fi
359 # run latest
360 install-my-scripts
361 # todo: consider changing this to srun and having the args come
362 # from a file like /etc/default/btrbk, like is done in exim
363 s jrun btrbk-run "$@"
364 if $active; then
365 if (( ret )); then
366 echo bbk: WARNING: btrbk.timer not restarted due to failure
367 else
368 ser start btrbk.timer
369 fi
370 fi
371 return $ret
372 }
373
374 faimon() {
375 fai-monitor | pee cat "fai-monitor-gui -"
376 }
377
378 bfg() { java -jar /a/opt/bfg-1.12.14.jar "$@"; }
379
380 bigclock() {
381 xclock -digital -update 1 -face 'arial black-80:bold'
382 }
383
384 nnn() { /a/opt/nnn -H "$@"; }
385
386 locat() { # log-once cat
387 local files
388 ngset
389 files=(/var/local/cron-errors/* /home/iank/cron-errors/* /sysd-mail-once-state/*)
390 case ${#files[@]} in
391 0) : ;;
392 1)
393 echo ${files[0]}
394 head ${files[0]}
395 ;;
396 *)
397 head ${files[@]}
398 ;;
399 esac
400 ngreset
401 }
402
403 # duplicated somewhat below.
404 jrun() { # journal run. run args, log to journal, tail and grep the journal.
405 # Note, an alternative without systemd would be something like ts.
406 # Note, I tried using systemd-cat, but this seems obviously better,
407 # and that seemed to have a problem exiting during a systemctl daemon-reload
408 local cmd_name jr_pid s
409 ret=0
410 cmd_name=${1##*/}
411 cmd=$1
412 if [[ $cmd != /* ]]; then
413 cmd=$(which $1)
414 fi
415 journalctl -qn2 -f -u "$cmd_name" &
416 # Guess of time needed to avoid missing initial lines.
417 # .5 was not reliable. 1 was not reliable. 2 was not reliable
418 sleep 3
419 # We kill this in prompt-command for the case that we ctrl-c the
420 # systemd-cat. i dont know any way to trap ctrl-c and still run the
421 # normal action for it. There might be a way, unsure.
422 jr_pid=$!
423 # note, we could have a version that does system --user, but if for example
424 # it does sudo ssh, that will leave a process around that we can't kill
425 # and it will leave the unit hanging around in a failed state needing manual
426 # killing of the process.
427 m s systemd-run --uid $(id -u) --gid $(id -g) \
428 -E SSH_AUTH_SOCK=/run/openssh_agent \
429 --unit "$cmd_name" --wait --collect "$cmd" "${@:2}" || ret=$?
430 # This justs lets the journal output its last line
431 # before the prompt comes up.
432 sleep .5
433 kill $jr_pid &>/dev/null ||:
434 unset jr_pid
435 fg &>/dev/null ||:
436 }
437 # service run, and watch the output
438 srun() {
439 local unit
440 ret=0
441 unit=$1
442 journalctl -qn2 -f -u $unit &
443 systemctl start $unit
444 sleep 2
445 kill $jr_pid &>/dev/null ||:
446 unset jr_pid
447 fg &>/dev/null ||:
448 }
449
450 sm() {
451 local tmp keyhash
452 c /
453 # run latest
454 keyhash=$(s ssh-keygen -lf /root/.ssh/home | awk '{print $2}')
455 tmp=$(s ssh-add -l | awk '$2 == "'$keyhash'"')
456 if [[ ! $tmp ]]; then
457 s ssh-add /root/.ssh/home
458 fi
459 install-my-scripts
460 s jrun switch-mail-host "$@"
461 return $ret
462 }
463
464 # shellcheck disable=SC2120
465 lipush() {
466 # note, i had --delete-excluded, but that deletes all files in --exclude-from on
467 # the remote site, which doesn't make sense, so not sure why i had it.
468 local p a
469 p=(/a/opt/{emacs-debian11{,-nox},mu,emacs} /a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts})
470 a="-ahviSAXPH --specials --devices --delete --relative --exclude-from=/p/c/li-rsync-excludes"
471 ret=0
472 for h in li je bk; do
473 m s rsync "$@" $a ${p[@]} /p/c/machine_specific/$h root@$h.b8.nz:/ || ret=$?
474 # only li is debian11
475 p[0]=/a/opt/emacs-ubuntu20.04
476 p[1]=/a/opt/emacs-ubuntu20.04-nox
477 done
478 m s rsync "$@" -ahviSAXPH root@li.b8.nz:/a/h/proposed-comments/ /a/h/proposed-comments || ret=$?
479 return $ret
480 }
481 bkpush() { # no emacs. for running faster.
482 p=(/a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts})
483 a="-ahviSAXPH --specials --devices --delete --relative --exclude-from=/p/c/li-rsync-excludes"
484 ret=0
485 m rsync "$@" $a ${p[@]} /p/c/machine_specific/bk root@bk.b8.nz:/ || ret=$?
486 return $ret
487 }
488 jepush() { # no emacs. for running faster.
489 p=(/a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts})
490 a="-ahviSAXPH --specials --devices --delete --relative --exclude-from=/p/c/li-rsync-excludes"
491 ret=0
492 m rsync "$@" $a ${p[@]} /p/c/machine_specific/je root@je.b8.nz:/ || ret=$?
493 return $ret
494 }
495
496 bindpush() {
497 dsign iankelling.org expertpathologyreview.com zroe.org amnimal.ninja
498 lipush
499 for h in li bk; do
500 m sl $h <<'EOF'
501 source ~/.bashrc
502 m dnsup
503 EOF
504 done
505 }
506 bindpushb8() {
507 lipush
508 for h in li bk; do
509 m sl $h <<'EOF'
510 source ~/.bashrc
511 m dnsb8
512 EOF
513 done
514 }
515
516 dnsup() {
517 conflink -f
518 m ser reload bind9
519 }
520 dnsb8() {
521 local f=/var/lib/bind/db.b8.nz
522 m ser stop bind9
523 m sleep 1
524 m sudo rm -fv $f.jnl
525 m sudo install -m 644 -o bind -g bind /p/c/machine_specific/vps/bind-initial/db.b8.nz $f
526 m ser restart bind9
527 }
528 dnsecgen() {
529 # keys generated like this
530 # because of https://ftp.isc.org/isc/dnssec-guide/dnssec-guide.pdf
531 # https://blog.apnic.net/2019/05/23/how-to-deploying-dnssec-with-bind-and-ubuntu-server/
532
533 # key length is longer than that guide because
534 # we are using those at fsf and when old key lengths
535 # become insecure, I want some extra time to update.
536 # dnsecgen (in brc2)
537
538 local zone=$1
539 dnssec-keygen -a RSASHA256 -b 2048 $zone
540 dnssec-keygen -f KSK -a RSASHA256 -b 4096 $zone
541 for f in K$zone.*.key; do
542 # eg Kb8.nz.+008+47995.key tag=47995
543 # in dnsimple, you add the long string from this.
544 # in gandi, you add the long string from the .key file,
545 # then see that the digest matches the ds.
546 echo "tag is the number after DS"
547 dnssec-dsfromkey -a SHA-256 $f
548 done
549 # For b8.nz, we let bind read the keys and sign, and
550 # right now they have root ownership, so let them
551 # get group read.
552 chmod g+r *.private
553 }
554 dsign() {
555 # create .signed file
556 # note: full paths probably not needed.
557 local arg
558 for arg; do
559 local zone=${arg#db.}
560 local dir=/p/c/machine_specific/vps/filesystem/var/lib/bind
561 dnssec-signzone -S -e +31536000 -o $zone -K $dir -d $dir $dir/db.$zone
562 done
563 }
564
565
566 #### begin bitcoin related things
567 btc() {
568 local f=/etc/bitcoin/bitcoin.conf
569 # importprivkey will timeout if using the default of 15 mins.
570 # upped it to 1 hour.
571 bitcoin-cli -rpcclienttimeout=60000 -$(s grep rpcuser= $f) -$(s grep rpcpassword= $f) "$@"
572 }
573 btcusd() { # $1 btc in usd
574 local price
575 price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
576 printf "$%s\n" "$price"
577 if [[ $1 ]]; then
578 printf "$%.2f\n" "$(echo "scale=4; $price * $1"| bc -l)"
579 fi
580 }
581 usdbtc() { # $1 usd in btc
582 local price
583 price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
584 printf "$%s\n" "$price"
585 if [[ $1 ]]; then
586 # 100 mil satoshi / btc. 8 digits after the 1.
587 printf "%.8f btc\n" "$(echo "scale=10; $1 / $price "| bc -l)"
588 fi
589 }
590 satoshi() { # $1 satoshi in usd
591 local price
592 price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
593 price=$(echo "scale=10; $price * 0.00000001"| bc -l)
594 printf "$%f\n" "$price"
595 if [[ $1 ]]; then
596 printf "$%.2f\n" "$(echo "scale=10; $price * $1"| bc -l)"
597 fi
598 }
599 #### end bitcoin related things
600
601
602
603 cbfstool () { /a/opt/coreboot/build/cbfstool "$@"; }
604
605
606 cgpl()
607 {
608 if (($#)); then
609 cp /a/bin/data/COPYING "$@"
610 else
611 cp /a/bin/data/COPYING .
612 fi
613 }
614
615 capache()
616 {
617 if (($#)); then
618 cp /a/bin/data/LICENSE "$@"
619 else
620 cp /a/bin/data/LICENSE .
621 fi
622 }
623
624 chrome() {
625 if type -p chromium &>/dev/null; then
626 cmd=chromium
627 else
628 cd /
629 cmd="schroot -c bullseye chromium"
630 CHROMIUM_FLAGS='--enable-remote-extensions' $cmd &r
631 fi
632 }
633
634
635 # do all tee.
636 # pipe to this, or just type like a shell
637 # todo: test this
638 dat() {
639 tee >(ssh frodo.b8.nz) >(ssh x2) >(ssh tp.b8.nz) >(ssh kw) >(ssh tp.b8.nz)
640 }
641 da() { # do all
642 local host
643 for host in x2 kw tp.b8.nz x3.b8.nz frodo.b8.nz; do
644 ssh $host "$@"
645 done
646 }
647
648
649 debian_pick_mirror () {
650 # netselect-apt finds a fast mirror.
651 # but we need to replace the mirrors ourselves,
652 # because it doesnt do that. best it can do is
653 # output a basic sources file
654 # here we get the server it found, get the main server we use
655 # then substitute all instances of one for the other in the sources file
656 # and backup original to /etc/apt/sources.list-original.
657 # this is idempotent. the only way to identify debian sources is to
658 # note the original server, so we put it in a comment so we can
659 # identify it later.
660 local file
661 file=$(mktemp -d)/f # safe way to get file name without creating one
662 sudo netselect-apt -o "$file" || return 1
663 url=$(grep ^\\w $file | head -n1 | awk '{print $2}')
664 sudo cp -f /etc/apt/sources.list /etc/apt/sources.list-original
665 sudo sed -ri "/http.us.debian.org/ s@( *[^ #]+ +)[^ ]+([^#]+).*@\1$url\2# http.us.debian.org@" /etc/apt/sources.list
666 sudo apt-get update
667 }
668 digme() {
669 digdiff @ns{1,2}.iankelling.org "$@"
670 }
671
672 tsr() { # ts run
673 "$@" |& ts || return $?
674 }
675
676 dup() {
677 local ran_d
678 ran_d=false
679 system-status _
680 case $PS1 in
681 *[\ \]]D\ *)
682 pushd /
683 /b/ds/distro-begin |& ts || return $?
684 /b/ds/distro-end |& ts || return $?
685 popd
686 ran_d=true
687 ;;&
688 *[\ \]]DB\ *)
689 pushd /
690 /b/ds/distro-begin |& ts || return $?
691 popd
692 ran_d=true
693 ;;
694 *[\ \]]DE\ *)
695 pushd /
696 /b/ds/distro-end |& ts || return $?
697 popd
698 ran_d=true
699 ;;&
700 *CONFLINK*)
701 if ! $ran_d; then
702 conflink
703 fi
704 ;;
705 esac
706 system-status _
707 }
708
709 envload() { # load environment from a previous: export > file
710 local file=${1:-$HOME/.${USER}_env}
711 eval "$(export | sed 's/^declare -x/export -n/')"
712 while IFS= read -r line; do
713 # declare -x makes variables local to a function
714 eval ${line/#declare -x/export}
715 done < "$file"
716 }
717
718 failfunc() { asdf a b c; }
719 failfunc2() { failfunc d e f; }
720
721 # one that comes with distros is too old for newer devices
722 fastboot() {
723 /a/opt/android-platform-tools/fastboot "$@";
724 }
725
726 kdecd() { /usr/lib/x86_64-linux-gnu/libexec/kdeconnectd; }
727
728 # List of apps to install/update
729 # Create from existing manually installed apps by doing
730 # fdroidcl update
731 # fdroidcl search -i, then manually removing
732 # automatically installed/preinstalled apps
733
734 #
735 # # my attempt at recovering from boot loop:
736 # # in that case, boot to recovery (volume up, home button, power, let go of power after samsun logo)
737 # # then
738 # mount /dev/block/mmcblk0p12 /data
739 # cd /data
740 # find -iname '*appname*'
741 # rm -rf FOUND_DIRS
742 # usually good enough to just rm -rf /data/app/APPNAME
743 #
744 # currently broken:
745 # # causes replicant to crash
746 # org.quantumbadger.redreader
747 # org.kde.kdeconnect_tp
748
749 # not broke, but wont work without gps
750 #com.zoffcc.applications.zanavi
751 # not broke, but not using atm
752 #com.nutomic.syncthingandroid
753 # # doesn\'t work on replicant
754 #net.sourceforge.opencamera
755 #
756 fdroid_pkgs=(
757 net.mullvad.mullvadvpn
758 org.schabi.newpipe
759 io.github.subhamtyagi.lastlauncher
760 io.anuke.mindustry
761 com.biglybt.android.client
762 de.marmaro.krt.ffupdater
763 me.ccrama.redditslide
764 org.fedorahosted.freeotp
765 at.bitfire.davdroid
766 com.alaskalinuxuser.justnotes
767 com.artifex.mupdf.viewer.app
768 com.danielkim.soundrecorder
769 com.fsck.k9
770 com.ichi2.anki
771 com.jmstudios.redmoon
772 com.jmstudios.chibe
773 org.kde.kdeconnect_tp
774 com.notecryptpro
775 com.termux
776 cz.martykan.forecastie
777 de.danoeh.antennapod
778 de.blinkt.openvpn
779 de.marmaro.krt.ffupdater
780 eu.siacs.conversations
781 free.rm.skytube.oss
782 im.vector.alpha # riot
783 info.papdt.blackblub
784 me.tripsit.tripmobile
785 net.gaast.giggity
786 net.minetest.minetest
787 net.osmand.plus
788 org.isoron.uhabits
789 org.linphone
790 org.gnu.icecat
791 org.smssecure.smssecure
792 org.yaaic
793 sh.ftp.rocketninelabs.meditationassistant.opensource
794 )
795 # https://forum.xda-developers.com/android/software-hacking/wip-selinux-capable-superuser-t3216394
796 # for maru,
797 #me.phh.superuser
798
799 fdup() {
800 local -A installed updated
801 local p
802 # tried putting this in go buildscript cronjob,
803 # but it failed with undefined: os.UserCacheDir. I expect its due to
804 # an environment variable missing, but its easier just to stick it here.
805 m go get -u mvdan.cc/fdroidcl || return 1
806 m fdroidcl update
807 if fdroidcl search -u | grep ^org.fdroid.fdroid; then
808 fdroidcl install org.fdroid.fdroid
809 sleep 5
810 m fdroidcl update
811 fi
812 for p in $(fdroidcl search -i| grep -o "^\S\+"); do
813 installed[$p]=true
814 done
815 for p in $(fdroidcl search -u| grep -o "^\S\+"); do
816 updated[$p]=false
817 done
818 for p in ${fdroid_pkgs[@]}; do
819 if ! ${installed[$p]:-false}; then
820 m fdroidcl install $p
821 # sleeps are just me being paranoid since replicant has a history of crashing when certain apps are installed
822 sleep 5
823 fi
824 done
825 for p in ${!installed[@]}; do
826 if ! ${updated[$p]:-true}; then
827 m fdroidcl install $p
828 sleep 5
829 fi
830 done
831 }
832
833 firefox-default-profile() {
834 key=Default value=1 section=$1
835 file=/p/c/subdir_files/.mozilla/firefox/profiles.ini
836 sed -ri "/^ *$key/d" "$file"
837 sed -ri "/ *\[$section\]/,/^ *\[[^]]+\]/{/^\s*$key[[:space:]=]/d};/ *\[$section\]/a $key=$value" "$file"
838 }
839 fdhome() { #firefox default home profile
840 firefox-default-profile Profile0
841 }
842
843 fdwork() {
844 firefox-default-profile Profile4
845 }
846
847 ff() {
848 if type -P firefox &>/dev/null; then
849 firefox "$@"
850 else
851 iceweasel "$@"
852 fi
853 }
854
855 fn() {
856 firefox -P alt "$@" >/dev/null 2>&1
857 }
858
859
860 fsdiff () {
861 local missing=false
862 local dname="${PWD##*/}"
863 local m="/a/tmp/$dname-missing"
864 local d="/a/tmp/$dname-diff"
865 [[ -e $d ]] && rm "$d"
866 [[ -e $m ]] && rm "$m"
867 local msize=0
868 local fsfile
869 while read -r line; do
870 fsfile="$1${line#.}"
871 if [[ -e "$fsfile" ]]; then
872 md5diff "$line" "$fsfile" && tee -a "/a/tmp/$dname-diff" <<< "$fsfile $line"
873 else
874 missing=true
875 echo "$line" >> "$m"
876 msize=$((msize + 1))
877 fi
878 done < <(find . -type f )
879 if $missing; then
880 echo "$m"
881 (( msize <= 100 )) && cat $m
882 fi
883 }
884 fsdiff-test() {
885 # expected output, with different tmp dirs
886 # /tmp/tmp.HDPbwMqdC9/c/d ./c/d
887 # /a/tmp/tmp.qLDkYxBYPM-missing
888 # ./b
889 cd $(mktemp -d)
890 echo ok > a
891 echo nok > b
892 mkdir c
893 echo ok > c/d
894 local x
895 x=$(mktemp -d)
896 mkdir $x/c
897 echo different > $x/c/d
898 echo ok > $x/a
899 fsdiff $x
900 }
901 rename-test() {
902 # test whether missing files were renamed, generally for use with fsdiff
903 # $1 = fsdiff output file, $2 = directory to compare to. pwd = fsdiff dir
904 # echos non-renamed files
905 local x y found
906 unset sums
907 for x in "$2"/*; do
908 { sums+=( "$(md5sum < "$x")" ) ; } 2>/dev/null
909 done
910 while read -r line; do
911 { missing_sum=$(md5sum < "$line") ; } 2>/dev/null
912 renamed=false
913 for x in "${sums[@]}"; do
914 if [[ $missing_sum == "$x" ]]; then
915 renamed=true
916 break
917 fi
918 done
919 $renamed || echo "$line"
920 done < "$1"
921 return 0
922 }
923
924 feh() {
925 # F = fullscren, z = random, Z = auto zoom
926 command feh -FzZ "$@"
927 }
928
929
930
931 fw() {
932 firefox -P default "$@" >/dev/null 2>&1
933 }
934
935 gitian() {
936 git config user.email ian@iankelling.org
937 }
938
939 # at least in flidas, things rely on gpg being gpg1
940 gpg() {
941 if type -P gpg2 &>/dev/null; then
942 command gpg2 "$@"
943 else
944 command gpg "$@"
945 fi
946 }
947
948 gse() {
949 local email=ian@iankelling.org
950 git send-email --notes "--envelope-sender=<$email>" \
951 --suppress-cc=self "$@"
952 }
953
954 gup() { /a/f/gnulib/build-aux/gnupload "$@"; }
955
956 dejagnu() { /a/opt/dejagnu/dejagnu "$@"; }
957
958 hstatus() {
959 # do git status on published repos.
960 c /a/bin/githtml
961 for x in *; do
962 cd $(readlink -f $x)/..
963 status=$(i status -s) || pwd
964 if [[ $status ]]; then
965 hr
966 echo $x
967 printf "%s\n" "$status"
968 fi
969 cd /a/bin/githtml
970 done
971 }
972
973 # work log
974 wlog() {
975 local day now i
976 for (( i=0; i<60; i++ )); do
977 day=$( date +%F -d @$((EPOCHSECONDS - 86400*i )) )
978 date "+%a %b %d" -d @$((EPOCHSECONDS - 86400*i )) | tr '\n' ' '
979 /a/opt/timetrap/bin/t d -ftotal -s $day -e $day all -m '^w|lunch$'
980 done
981 }
982 to() { t out -a "$@"; }
983 ti() { t in -a "$@"; }
984 tl() {
985 to "$*"
986 t s lunch
987 t in -a "$*"
988 m t out -a $(date +%F.%T -d @$(( $(date -d "$(echo $*|sed 's/[_.]/ /g')" +%s) + 60*45 )) )
989 t s w
990 }
991
992 arbttlog() { arbtt-dump "$@" | grep -v '( )\|Current Desktop' | sed -rn '/^[^ ]/{N;s/^(.{21})([0-9]*)[0-9]{3}m.*\(\*/\1\2/;s/^(.{21})[0-9]*.*\(\*/\1/;s/\n//;p}' ; }
993
994 idea() {
995 /a/opt/idea-IC-163.7743.44/bin/idea.sh "$@" &r
996 }
997
998 ilogs() {
999 ssh root@iankelling.org "cd /var/lib/znc/moddata/log/iank/freenode/ && hr && for x in \#$1/*; do base=\${x##*/}; files=(); for f in $@; do tmp=\#\$f/\$base; if [[ -e \$tmp ]]; then files+=(\#\$f/\$base); fi; done; sed \"s/^./\${base%log}/\" \${files[@]}|sort -n; hr; done"
1000 }
1001
1002 ilog() {
1003 chan=${1:-#fsfsys}
1004 # use * instead of -r since that does sorted order
1005 ssh root@iankelling.org "for n in freenode libera; do cd /var/lib/znc/moddata/log/iank/\$n/$chan && hr && for x in *; do echo \$x; sed \"s/^./\${x%log}/\" \$x; hr; done; done" | less +G
1006 }
1007
1008 o() {
1009 if type gio &> /dev/null ; then
1010 gio open "$@"
1011 elif type gvfs-open &> /dev/null ; then
1012 gvfs-open "$@"
1013 else
1014 xdg-open "$@"
1015 fi
1016 # another alternative is run-mailcap
1017 }
1018 ccomp xdg-open o
1019
1020 # jfilter() {
1021 # grep -Evi -e "^(\S+\s+){4}(sudo|sshd|cron)\[\S*:" \
1022 # -e "^(\S+\s+){4}systemd\[\S*: (starting|started) (btrfsmaintstop|dynamicipupdate|spamd dns bug fix cronjob|rss2email)\.*$"
1023 # }
1024 # jtail() {
1025 # journalctl -n 10000 -f "$@" | jfilter
1026 # }
1027 # jr() { journalctl "$@" | jfilter | less ; }
1028 # jrf() { journalctl -n 200 -f "$@" | jfilter; }
1029
1030 jr() { journalctl "$@" ; }
1031 jrf() { journalctl -n 200 -f "$@" ; }
1032
1033
1034 ccomp journalctl jtail jr jrf
1035
1036 kff() { # keyboardio firmware flash
1037 pushd /a/bin/distro-setup/Arduino/Model01-Firmware
1038 yes $'\n' | make flash
1039 popd
1040 }
1041
1042 wgkey() {
1043 local umask_orig name
1044 if (( $# != 1 )); then
1045 e expected 1 arg >&2
1046 return 1
1047 fi
1048 name=$1
1049 umask_orig=$(umask)
1050 umask 0077
1051 wg genkey | tee $name-priv.key | wg pubkey > $name-pub.key
1052 umask $umask_orig
1053 }
1054 wghole() {
1055 if (( $# != 2 )); then
1056 e expected 2 arg of hostname, ip suffix >&2
1057 return 1
1058 fi
1059 local host ipsuf umask_orig
1060 host=$1
1061 ipsuf=$2
1062 mkdir -p /p/c/machine_specific/$host/filesystem/etc/wireguard
1063 cd /p/c/machine_specific/$host/filesystem/etc/wireguard
1064 umask_orig=$(umask)
1065 umask 0077
1066 wg genkey | tee hole-priv.key | wg pubkey > hole-pub.key
1067 cat >wghole.conf <<EOF
1068 [Interface]
1069 # contents hole-priv.key
1070 PrivateKey = $(cat hole-priv.key)
1071 ListenPort = 1194
1072 Address = 10.8.0.$ipsuf/24
1073 # https://dev.to/tangramvision/what-they-don-t-tell-you-about-setting-up-a-wireguard-vpn-1h2g
1074 # ||: makes the systemd service not fail due to the failed command
1075 PostUp = ping -c1 10.8.0.1 ||:
1076
1077 [Peer]
1078 # li. called wgmail on that server
1079 PublicKey = CTFsje45qLAU44AbX71Vo+xFJ6rt7Cu6+vdMGyWjBjU=
1080 AllowedIPs = 10.8.0.0/24
1081 Endpoint = 72.14.176.105:1194
1082 PersistentKeepalive = 25
1083 EOF
1084 umask $umask_orig
1085 # old approach. systemd seems to work fine and cleaner.
1086 rm -f ../network/interfaces.d/wghole
1087 cedit -q $host /p/c/machine_specific/li/filesystem/etc/wireguard/wgmail.conf <<EOF || [[ $? == 1 ]]
1088 [Peer]
1089 PublicKey = $(cat hole-pub.key)
1090 AllowedIPs = 10.8.0.$ipsuf/32
1091 EOF
1092 cd - >/dev/null
1093 }
1094
1095
1096 lom() {
1097 local l base
1098 if [[ $1 == /* ]]; then
1099 base=${1##*/}
1100 if mountpoint -q /mnt/$base; then
1101 return 0
1102 fi
1103 l=$(losetup -j $1 | sed -rn 's/^([^ ]+): .*/\1/p' | head -n1 ||:)
1104 if [[ ! $l ]]; then
1105 l=$(sudo losetup -f)
1106 m sudo losetup $l $1
1107 fi
1108 if ! sudo cryptsetup status /dev/mapper/$base &>/dev/null; then
1109 if ! sudo cryptsetup luksOpen $l $base; then
1110 m sudo losetup -d $l
1111 return 1
1112 fi
1113 fi
1114 m sudo mkdir -p /mnt/$base
1115 m sudo mount /dev/mapper/$base /mnt/$base
1116 m sudo chown $USER:$USER /mnt/$base
1117 else
1118 base=$1
1119 if mountpoint /mnt/$base &>/dev/null; then
1120 m sudo umount /mnt/$base
1121 fi
1122 if sudo cryptsetup status /dev/mapper/$base &>/dev/null; then
1123 if ! m sudo cryptsetup luksClose /dev/mapper/$base; then
1124 echo lom: failed cryptsetup luksClose /dev/mapper/$base
1125 return 1
1126 fi
1127 fi
1128 l=$(losetup -l --noheadings | awk '$6 ~ /\/'$1'$/ {print $1}')
1129 if [[ $l ]]; then
1130 m sudo losetup -d $l
1131 else
1132 echo lom: warning: no loopback device found
1133 fi
1134 fi
1135 }
1136
1137 # mu personality. for original, just run mp. for 2, run mp 2.
1138 # this is partly duplicated in mail-setup
1139 mp() {
1140 local dead=false
1141 for s in {1..5}; do
1142 if ! killall mu; then
1143 dead=true
1144 break
1145 fi
1146 sleep 1
1147 done
1148 if ! $dead; then
1149 echo error: mu not dead
1150 m psg mu
1151 return 1
1152 fi
1153 suf=$1
1154 set -- /m/mucache ~/.cache/mu /m/.mu ~/.config/mu
1155 while (($#)); do
1156 target=$1$suf
1157 f=$2
1158 shift 2
1159 if [[ -e $f && ! -L $f ]]; then
1160 m rm -rf $f
1161 fi
1162 m ln -sf -T $target $f
1163 done
1164 }
1165
1166 # these might need a mu index or something added.
1167 mbenable() {
1168 local mb=$1
1169 dst=/m/4e/$mb
1170 src=/m/md/$mb
1171 [[ -e $src ]] || { echo "src:$src does not exist"; return 1; }
1172 m mv -T $src $dst
1173 m ln -s -T $dst $src
1174 }
1175 mb2enable() {
1176 local mb
1177 for mb; do
1178 dst=/m/4e2/$mb
1179 link=/m/md/$mb
1180 src=/m/md/$mb
1181 if [[ ! -e $src || -L $src ]]; then
1182 src=/m/4e/$mb
1183 fi
1184 [[ -e $src ]] || { echo "src:$src does not exist"; return 1; }
1185 m mv -T $src $dst
1186 m ln -sf -T $dst $link
1187 done
1188 }
1189 mbdisable() {
1190 local mb=$1
1191 dst=/m/md/$mb
1192 src=/m/4e/$mb
1193 set -x
1194 [[ -e $src ]] || { set +x; return 1; }
1195 if [[ -L $dst ]]; then rm $dst; fi
1196 mv -T $src $dst
1197 set +x
1198 }
1199
1200
1201 mdt() {
1202 markdown "$1" >/tmp/mdtest.html
1203 firefox /tmp/mdtest.html
1204 }
1205
1206 mo() { xset dpms force off; } # monitor off
1207
1208 mpvd() {
1209 mpv --profile=d "$@";
1210 }
1211 mpvs() {
1212 mpv --profile=s "$@";
1213 }
1214
1215 myirc() {
1216 if [[ ! $1 ]]; then
1217 set -- fsf-office
1218 fi
1219 local d1 d2
1220 d=( /var/lib/znc/moddata/log/iank/{freenode,libera} )
1221 # use * instead of -r since that does sorted order
1222 ssh root@iankelling.org "for f in ${d[@]}; do cd \$f/#$1; grep '\<iank.*' *; done" | cut --complement -c12-16
1223 }
1224 mypidgin() {
1225 c /p/c/.purple/logs/jabber/iank@fsf.org/office@conference.fsf.org.chat
1226 for x in *.html; do html2text -o ${x%.html}.txt $x; done;
1227 grep -A1 ') iank:' *.txt | sed -r 's/^(.{10})[^ ]*\.txt:\(?([^ ]*)[[:space:]](..). iank:/\1_\2_\3/;s/^[^ ]*\.txt-//;/^--$/d;s/^[^ ]*\.txt:\((.{2}).(.{2}).(.{4}) (.{8}) (.{2})\)?/\3-\1-\2_\4_\5/' | sed -n 'x;1d;0~2{G;s/\n/ /;p};${x;p}'
1228 }
1229 allmyirc() {
1230 local d
1231 d=/var/lib/znc/moddata/log/iank/freenode
1232 ssh root@iankelling.org "cd $d; find . -mtime -60 -type f -exec grep '\<iank.*' {} +" | sed -r 's,^..([^/]*)/(.{11})(.{5})(.{8}).,\2\4 \1,' | sort
1233 }
1234
1235 mygajim() {
1236 local time time_sec time_pretty
1237 sqlite3 -separator ' ' /p/c/subdir_files/.local/share/gajim/logs.db "select time, message from logs where contact_name = 'iank' and jid_id = 17;" | while read -r time l; do
1238 case $time in
1239 16*) : ;;
1240 *) continue ;;
1241 esac
1242 if ! time_pretty=$(date +%F.%R -d @$time); then
1243 echo bad time: $time
1244 return 1
1245 fi
1246 echo $time_pretty "$l"
1247 time_sec=${time%%.*}
1248 # only look at the last 18 days. generally just use this for timesheet.
1249 if (( time_sec < EPOCHSECONDS - 60 * 60 * 24 * 18 )); then break; fi
1250 done
1251 }
1252
1253 gajlogs() {
1254 sqlite3 -separator ' ' /p/c/subdir_files/.local/share/gajim/logs.db "select time, message from logs" | less
1255 }
1256
1257 net-dev-info() {
1258 e "lspci -nnk|gr -iA2 net"
1259 lspci -nnk|gr -iA2 net
1260 hr
1261 e "s lshw -C network"
1262 hr
1263 sudo lshw -C network
1264 }
1265
1266 nk() {
1267 ser stop NetworkManager
1268 ser disable NetworkManager
1269 ser stop NetworkManager-wait-online.service
1270 ser disable NetworkManager-wait-online.service
1271 ser stop dnsmasq
1272 sudo resolvconf -d NetworkManager
1273 # ser start dnsmasq
1274 sudo ifup br0
1275 }
1276 ngo() {
1277 sudo ifdown br0
1278 ser start NetworkManager
1279 sleep 4
1280 sudo nmtui-connect
1281 }
1282
1283 otp() {
1284 oathtool --totp -b "$*" | xclip -selection clipboard
1285 }
1286 j() {
1287 "$@" |& pee "xclip -r -selection clipboard"
1288 }
1289
1290
1291 pakaraoke() {
1292 # from http://askubuntu.com/questions/456021/remove-vocals-from-mp3-and-get-only-instrumentals
1293 pactl load-module module-ladspa-sink sink_name=Karaoke master=alsa_output.usb-Audioengine_Audioengine_D1-00.analog-stereo plugin=karaoke_1409 label=karaoke control=-30
1294 }
1295
1296 pfind() { #find *$1* in $PATH
1297 [[ $# != 1 ]] && { echo requires 1 argument; return 1; }
1298 local pathArray
1299 IFS=: pathArray=($PATH); unset IFS
1300 find "${pathArray[@]}" -iname "*$1*"
1301 }
1302
1303 pick-trash() {
1304 # trash-restore lists everything that has been trashed at or below CWD
1305 # This picks out files just in CWD, not subdirectories,
1306 # which also match grep $1, usually use $1 for a time string
1307 # which you get from running restore-trash once first
1308 local name x ask
1309 local nth=1
1310 # last condition is to not ask again for ones we skipped
1311 while name="$( echo | restore-trash | gr "$PWD/[^/]\+$" | gr "$1" )" \
1312 && [[ $name ]] && (( $(wc -l <<<"$name") >= nth )); do
1313 name="$(echo "$name" | head -n $nth | tail -n 1 )"
1314 read -r -p "$name [Y/n] " ask
1315 if [[ ! $ask || $ask == [Yy] ]]; then
1316 x=$( echo "$name" | gr -o "^\s*[0-9]*" )
1317 echo $x | restore-trash > /dev/null
1318 elif [[ $ask == [Nn] ]]; then
1319 nth=$((nth+1))
1320 else
1321 return
1322 fi
1323 done
1324 }
1325
1326
1327 pub() {
1328 rld /a/h/_site/ li:/var/www/iankelling.org/html
1329 }
1330
1331
1332 pumpa() {
1333 # fixes the menu bar in xmonad. this won\'t be needed when xmonad
1334 # packages catches up on some changes in future (this is written in
1335 # 4/2017)
1336 #
1337 # geekosaur: so youll want to upgrade to xmonad 0.13 or else use a
1338 # locally modified XMonad.Hooks.ManageDocks that doesnt set the
1339 # work area; turns out it\'s impossible to set correctly if you are
1340 # not a fully EWMH compliant desktop environment
1341 #
1342 # geekosaur: chrome shows one failure mode, qt/kde another, other
1343 # gtk apps a third, ... I came up with a setting that works for me
1344 # locally but apparently doesnt work for others, so we joined the
1345 # other tiling window managers in giving up on setting it at all
1346 #
1347 xprop -root -remove _NET_WORKAREA
1348 command pumpa & r
1349 }
1350
1351 # reviewboard, used at my old job
1352 #rbpipe() { rbt post -o --diff-filename=- "$@"; }
1353 #rbp() { rbt post -o "$@"; }
1354
1355 rebr() {
1356 sudo ifdown br0
1357 sudo ifup br0
1358 }
1359
1360
1361 # only run on MAIL_HOST. simpler to keep this on one system.
1362 r2eadd() { # usage: name url
1363 # initial setup of rss2email:
1364 # r2e new r2e@iankelling.org
1365 # that initializes files, and sets default email.
1366 # symlink to the config doesnt work, so I copied it to /p/c
1367 # and then use cli option to specify explicit path.
1368 # Only option changed from default config is to set
1369 # force-from = True
1370 #
1371 # or else for a few feeds, the from address is set by the feed, and
1372 # if I fail delivery, then I send a bounce message to that from
1373 # address, which makes me be a spammer.
1374
1375 r2e add $1 "$2" $1@r2e.iankelling.org
1376 # get up to date and dont send old entries now:
1377 r2e run --no-send $1
1378 }
1379 r2e() { command r2e -d /p/c/rss2email.json -c /p/c/rss2email.cfg "$@"; }
1380
1381 rspicy() { # usage: HOST DOMAIN
1382 # connect to spice vm remote host. use vspicy for local host
1383 local port
1384 # shellcheck disable=SC2087
1385 port=$(ssh $1<<EOF
1386 sudo virsh dumpxml $2|grep "<graphics.*type='spice'" | \
1387 sed -rn "s/.*port='([0-9]+).*/\1/p"
1388 EOF
1389 )
1390 if [[ $port ]]; then
1391 spicy -h $1 -p $port
1392 else
1393 echo "error: no port found. check that the domain is running."
1394 fi
1395 }
1396
1397
1398 scssl() {
1399 # s gem install scss-lint
1400 pushd /a/opt/thoughtbot-guides
1401 git pull --stat
1402 popd
1403 scss-lint -c /a/opt/thoughtbot-guides/style/sass/.scss-lint.yml "$@"
1404 }
1405
1406 skbrc() {
1407 sk -e 2120,245 /b/ds/brc /b/ds/brc2
1408 }
1409
1410 skaraoke() {
1411 local tmp out
1412 out=${2:-${1%.*}.sh}
1413 tmp=$(mktemp -d)
1414 script -t -c "mpv --no-config --no-resume-playback --no-terminal --no-audio-display '$1'" $tmp/typescript 2>$tmp/timing
1415 # todo, the current sleep seems pretty good, but it
1416 # would be nice to have an empirical measurement, or
1417 # some better wait to sync up.
1418 #
1419 # note: --loop-file=no prevents it from hanging if you have that
1420 # set to inf the mpv config.
1421 # --loop=no prevents it from exit code 3 due to stdin if you
1422 # had it set to inf in mpv config.
1423 #
1424 # args go to mpv, for example --volume=80, 50%
1425 cat >$out <<EOFOUTER
1426 #!/bin/bash
1427 trap "trap - TERM && kill 0" INT TERM ERR; set -e
1428 ( sleep .2; scriptreplay <( cat <<'EOF'
1429 $(cat $tmp/timing)
1430 EOF
1431 ) <( cat <<'EOF'
1432 $(cat $tmp/typescript)
1433 EOF
1434 ))&
1435 base64 -d - <<'EOF'| mpv --loop=no --loop-file=no --no-terminal --no-audio-display "\$@" -
1436 $(base64 "$1")
1437 EOF
1438 kill 0
1439 EOFOUTER
1440 rm -r $tmp
1441 chmod +x $out
1442 }
1443
1444 smeld() { # ssh meld usage host1 host2 file
1445 meld <(ssh $1 cat $3) <(ssh $2 cat $3)
1446 }
1447
1448 spd() {
1449 PATH=/usr/local/spdhackfix:$PATH command spd "$@"
1450 }
1451
1452 spend() {
1453 sudo systemctl suspend
1454 }
1455
1456 spamf() { # spamtest on FILE
1457 local spamcpre spamdpid
1458
1459 if (( $# != 1 )); then
1460 e spamtest error: expected 1 arg, filename >&2
1461 return 1
1462 fi
1463
1464 spamdpid=$(systemctl status spamassassin| sed -n '/^ *Main PID:/s/[^0-9]//gp')
1465 spamcpre="nsenter -t $spamdpid -n -m"
1466 s $spamcpre sudo -u Debian-exim spamassassin -t --cf='score PYZOR_CHECK 0' <"$1"
1467 }
1468
1469
1470 # mail related
1471 testmail() {
1472 declare -gi _seq; _seq+=1
1473 echo "test body" | m mail -s "test mail from $HOSTNAME, $_seq" "${@:-root@localhost}"
1474 # for testing to send from an external address, you can do for example
1475 # -fian@iank.bid -aFrom:ian@iank.bid web-6fnbs@mail-tester.com
1476 # note in exim, you can retry a deferred message
1477 # s exim -M MSG_ID
1478 # MSG_ID is in /var/log/exim4/mainlog, looks like 1ccdnD-0001nh-EN
1479 }
1480
1481 # to test sieve, use below command. for fsf mail, see offlineimap-sync script
1482 # make modifications, then copy to live file, use -eW to actually modify mailbox
1483 #
1484 # Another option is to use sieve-test SCRIPT MAIL_FILE. note,
1485 # sieve-test doesnt know about envelopes, Im not sure if sieve-filter does.
1486
1487 # sieve with output filter. arg is mailbox, like INBOX.
1488 # This depends on dovecot conf, notably mail_location in /etc/dovecot/conf.d/10-mail.conf
1489
1490 # always run this first, edit the test files, then run the following
1491 testsieve() {
1492 sieve-filter ~/sieve/maintest.sieve ${1:-INBOX} delete 2> >(head; tail) >/tmp/testsieve.log && sed -rn '/^Performed actions:/,/^[^ ]/{/^ /p}' /tmp/testsieve.log | sort | uniq -c
1493 }
1494 runsieve() {
1495 c ~/sieve; cp personal{test,}.sieve; cp lists{test,}.sieve; cp personalend{test,}.sieve
1496 sieve-filter -eWv ~/sieve/maintest.sieve ${1:-INBOX} delete &> /tmp/testsieve.log
1497 sed -r '/^info: filtering:/{h;d};/^info: msgid=$/N;/^info: msgid=.*left message in mailbox [^ ]+$/d;/^info: msgid=/{H;g};/^info: message kept in source mailbox.$/d' /tmp/testsieve.log
1498 }
1499
1500 # usage:
1501 # alertme SUBJECT
1502 # printf "subject\nbody\n" | alertme
1503 alertme() {
1504 if [[ -t 0 ]]; then
1505 exim -t <<EOF
1506 From: alertme@b8.nz
1507 To: alerts@iankelling.org
1508 Subject: $*
1509 EOF
1510 else
1511 read sub
1512 { cat <<EOF
1513 From: alertme@b8.nz
1514 To: alerts@iankelling.org
1515 Subject: $sub
1516
1517 EOF
1518 cat
1519 } | exim -t
1520 fi
1521 }
1522 daylertme() {
1523 if [[ -t 0 ]]; then
1524 exim -t <<EOF
1525 From: alertme@b8.nz
1526 To: daylert@iankelling.org
1527 Subject: $*
1528 EOF
1529 else
1530 read sub
1531 { cat <<EOF
1532 From: alertme@b8.nz
1533 To: daylert@iankelling.org
1534 Subject: $sub
1535
1536 EOF
1537 cat
1538 } | exim -t
1539 fi
1540 }
1541
1542 # alert when a page goes live. not urgent.
1543 alert200() {
1544 url="$1"
1545 tmpdir="$(mktemp -d)"
1546 cd $tmpdir
1547 while true; do
1548 if torsocks wget -q "$url"; then
1549 alertme $tmpdir
1550 fi
1551 sleep $(( 600 + RANDOM % 300 ))
1552 done
1553 }
1554
1555
1556 # mail related
1557 testexim() {
1558 # testmail above calls sendmail, which is a link to exim/postfix.
1559 # its docs dont say a way of adding an argument
1560 # to sendmail to turn on debug output. We could make a wrapper, but
1561 # that is a pain. Exim debug args are documented here:
1562 # http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
1563 #
1564 # http://www.exim.org/exim-html-current/doc/html/spec_html/ch-building_and_installing_exim.html
1565 # note, for exim daemon, you can turn on debug options by
1566 # adding -d, etc to COMMONOPTIONS in
1567 # /etc/default/exim4
1568 #
1569 # to specify recipients other than those in to, cc, bcc, you can use the cli args, eg:
1570 # exim -t 'test@zroe.org, t2@zroe.org' <<'EOF'
1571 #
1572 # -t = get recipient from header
1573 exim -d -t <<'EOF'
1574 From: i@dmarctest.b8.nz
1575 To: mailman@dev.fsf.org
1576 Subject: test2
1577 Reply-to: rtest@iankelling.org
1578
1579 This is a test message.
1580 EOF
1581 }
1582
1583 # toggle keyboard
1584 tk() {
1585 # based on
1586 # https://askubuntu.com/questions/160945/is-there-a-way-to-disable-a-laptops-internal-keyboard
1587 id=$(xinput --list --id-only 'AT Translated Set 2 keyboard')
1588 if xinput list | grep -F '∼ AT Translated Set 2 keyboard' &>/dev/null; then
1589 echo enabling keyboard
1590 # find the first slave keyboard number, they are all the same in my output.
1591 # if they werent, worst case we would need to save the slave number somewhere
1592 # when it got disabled.
1593 slave=$(xinput list | sed -n 's/.*slave \+keyboard (\([0-9]*\)).*/\1/p' | head -n1)
1594 xinput reattach $id $slave
1595 else
1596 xinput float $id
1597 fi
1598 }
1599
1600 tm() {
1601 # timer in minutes
1602 # --no-config
1603 (sleep $(calc "$* * 60") && mpv --no-config --volume 50 /a/bin/data/alarm.mp3) > /dev/null 2>&1 &
1604 }
1605
1606 trg() { transmission-remote-gtk & r; }
1607 trc() {
1608 # example, set global upload limit to 100 kilobytes:
1609 # trc -u 100
1610 TR_AUTH=":$(jq -r .profiles[0].password ~/.config/transmission-remote-gtk/config.json)" transmission-remote transmission.lan -ne "$@"
1611 }
1612
1613 trysleep() {
1614 retries="$1"
1615 sleepsecs="$2"
1616 shift 2
1617 for (( i=0; i < retries - 1; i++ )); do
1618 if "$@"; then
1619 return 0
1620 fi
1621 sleep $sleepsecs
1622 done
1623 "$@"
1624 }
1625
1626
1627 tu() {
1628 local s
1629 if [[ -e $1 && ! -w $1 || ! -w $(dirname "$1") ]]; then
1630 s=s;
1631 fi
1632 # full path for using in some initial setup steps
1633 $s /a/exe/teeu "$@"
1634 }
1635
1636 enn() {
1637 local ecmd pid
1638
1639 ecmd="/usr/sbin/exim4 -C /etc/exim4/my.conf"
1640 if ip a show veth1-mail &>/dev/null; then
1641 s $ecmd "$@"
1642 return
1643 fi
1644 pid=$(pgrep -f "/usr/sbin/exim4 -bd -q30m -C /etc/exim4/my.conf"|h1)
1645 m s nsenter -t $pid -n -m $ecmd "$@"
1646 }
1647
1648 sdnbash() { # systemd namespace bash
1649 local unit=$1
1650 m sudo nsenter -t $(systemctl show --property MainPID --value $unit) -n -m sudo -u $USER -i bash
1651 }
1652
1653 mailnnbash() {
1654 m sudo nsenter -t $(systemctl show --property MainPID --value mailnn) -n -m sudo -u $USER -i bash
1655 }
1656
1657 mailvpnbash() {
1658 m sudo nsenter -t $(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/.*mail.conf") -n -m sudo -u $USER -i bash
1659 }
1660 eximbash() {
1661 local pid
1662 pid=$(pgrep -f "/usr/sbin/exim4 -bd -q30m -C /etc/exim4/my.conf"|h1)
1663 if [[ ! $pid ]]; then
1664 echo "eximbash: failed to find exim pid. systemctl -n 30 status exim4:"
1665 systemctl status exim4
1666 fi
1667 m sudo nsenter -t $pid -n -m
1668 }
1669 spamnn() {
1670 local spamdpid
1671 spamdpid=$(systemctl show --property MainPID --value spamassassin)
1672 m sudo nsenter -t $spamdpid -n -m sudo -u Debian-exim spamassassin "$@"
1673 }
1674 unboundbash() {
1675 m sudo nsenter -t $(systemctl status unbound| sed -n '/^ *Main PID:/s/[^0-9]//gp') -n -m sudo -u $USER -i bash
1676 }
1677
1678 mailnncheck() {
1679 local p pid ns mailnn
1680 # mailvpn would belong on the list if using openvpn
1681 for p in mailnn unbound dovecot spamassassin exim4 radicale; do
1682 case $p in
1683 exim4|radicale)
1684 pid=$(ps -eo pid,cgroup | grep /system.slice/$p.service | awk '{print $1}')
1685 ;;
1686 *)
1687 pid=$(s systemctl show --property MainPID --value $p)
1688 ;;
1689 esac
1690 echo p=$p pid=$pid
1691 if [[ ! $pid ]]; then
1692 echo failed to find pid for $p
1693 continue
1694 fi
1695 if ! ns=$(s readlink /proc/$pid/ns/net); then
1696 echo failed to find ns for $p pid=$pid
1697 continue
1698 fi
1699 if [[ $mailnn ]]; then
1700 if [[ $ns != "$mailnn" ]]; then
1701 echo "$p ns $ns != $mailnn"
1702 fi
1703 else
1704 mailnn=$ns
1705 fi
1706 done
1707
1708 }
1709
1710
1711 vpncmd() {
1712 m sudo -E env "PATH=$PATH" nsenter -t $(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/.*client.conf") -n -m "$@"
1713 }
1714 vpnf() {
1715 vpncmd sudo -E -u iank env "PATH=$PATH" abrowser -no-remote -P vpn & r
1716 }
1717 vpn2f() {
1718 vpncmd sudo -u iank env "PATH=$PATH" abrowser -no-remote -P vpn2 & r
1719 }
1720
1721 vpni() {
1722 vpncmd sudo -u iank env "PATH=$PATH" "$@"
1723 }
1724 vpnbash() {
1725 vpncmd bash
1726 }
1727
1728
1729 vpn() {
1730 if [[ -e /lib/systemd/system/openvpn-client@.service ]]; then
1731 local vpn_service=openvpn-client
1732 else
1733 local vpn_service=openvpn
1734 fi
1735
1736 [[ $1 ]] || { echo need arg; return 1; }
1737 journalctl --unit=$vpn_service@$1 -f -n0 &
1738 # sometimes the journal doesnt open until after the vpn output
1739 # has happened. hoping this fixes that.
1740 sleep 1
1741 sudo systemctl start $vpn_service@$1
1742 # sometimes the ask-password agent does not work and needs a delay.
1743 sleep .5
1744 # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=779240
1745 # noticed around 8-2017 after update from around stretch release
1746 # on debian testing, even though the bug is much older.
1747 sudo systemd-tty-ask-password-agent
1748 }
1749
1750 fixu() {
1751 ls -lad /run/user/1000
1752 s chmod 700 /run/user/1000; s chown iank.iank /run/user/1000
1753 }
1754
1755 # systemctl is-enabled / status / cat says nothing, instead theres
1756 # some obscure symlink. paths copied from man systemd.unit.
1757 # possibly also usefull, but incomplete, doesnt show units not loaded in memory:
1758 # seru list-dependencies --reverse --all UNIT
1759 sysd-deps() {
1760 local f
1761 local -a dirs search
1762 ngset
1763
1764 case $1 in
1765 u)
1766 search=(
1767 ~/.config/systemd/user.control/*
1768 $XDG_RUNTIME_DIR/systemd/user.control/*
1769 $XDG_RUNTIME_DIR/systemd/transient/*
1770 $XDG_RUNTIME_DIR/systemd/generator.early/*
1771 ~/.config/systemd/user/*
1772 /etc/systemd/user/*
1773 $XDG_RUNTIME_DIR/systemd/user/*
1774 /run/systemd/user/*
1775 $XDG_RUNTIME_DIR/systemd/generator/*
1776 ~/.local/share/systemd/user/*
1777 /usr/lib/systemd/user/*
1778 $XDG_RUNTIME_DIR/systemd/generator.late/*
1779 )
1780 ;;
1781 *)
1782 search=(
1783 /etc/systemd/system.control/*
1784 /run/systemd/system.control/*
1785 /run/systemd/transient/*
1786 /run/systemd/generator.early/*
1787 /etc/systemd/system/*
1788 /etc/systemd/systemd.attached/*
1789 /run/systemd/system/*
1790 /run/systemd/systemd.attached/*
1791 /run/systemd/generator/*
1792 /lib/systemd/system/*
1793 /run/systemd/generator.late/*
1794 )
1795 ;;
1796 esac
1797 for f in "${search[@]}"; do
1798 [[ -d $f ]] || continue
1799 case $f in
1800 *.requires|*.wants)
1801 dirs+=("$f")
1802 ;;
1803 esac
1804 done
1805 # dirs is just so we write out the directory names, ls does it when there is 2 or more dirs.
1806 case ${#dirs[@]} in
1807 1)
1808 echo "${dirs[0]}:"
1809 ll "${dirs[@]}"
1810 ;;
1811 0) : ;;
1812 *)
1813 ll "${dirs[@]}"
1814 ;;
1815 esac
1816 ngreset
1817 }
1818
1819 fixvpndns() {
1820 local link istls
1821 read _ link _ istls < <(resolvectl dnsovertls tunfsf)
1822 case $istls in
1823 yes|no) : ;;
1824 *) echo fixvpndns error: unexpected istls value: $istls >&2; return 1 ;;
1825 esac
1826 s busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager SetLinkDNSOverTLS is $link no
1827 }
1828
1829 vpnoff() {
1830 [[ $1 ]] || { echo need arg; return 1; }
1831 if [[ -e /lib/systemd/system/openvpn-client@.service ]]; then
1832 local vpn_service=openvpn-client
1833 else
1834 local vpn_service=openvpn
1835 fi
1836 sudo systemctl stop $vpn_service@$1
1837 }
1838 vpnoffc() { # vpn off client
1839 ser stop openvpn-client-tr@client
1840 }
1841 vpnc() {
1842 ser start openvpn-client-tr@client
1843 }
1844
1845
1846 vspicy() { # usage: VIRSH_DOMAIN
1847 # connect to vms made with virt-install
1848 spicy -p $(sudo virsh dumpxml "$1"|grep "<graphics.*type='spice'"|\
1849 sed -r "s/.*port='([0-9]+).*/\1/")
1850 }
1851
1852 wian() {
1853 cat-new-files /m/4e/INBOX/new
1854 }
1855
1856 wtr() { curl wttr.in/boston; }
1857
1858 xevkb() { xev -event keyboard; }
1859
1860 # * misc stuff
1861
1862 vrun() {
1863 printf "running: %s\n" "$*"
1864 "$@"
1865 }
1866
1867 f=/a/f/ansible-configs/files/common/etc/fsf-workstation-bashrc.sh
1868 if [[ -e $f ]]; then
1869 # shellcheck disable=SC1090
1870 source $f
1871 fi
1872
1873
1874
1875
1876 reset-konsole() {
1877 # we also have a file in /a/c/...konsole...
1878 local f=$HOME/.config/konsolerc
1879 setini DefaultProfile profileian.profile "Desktop Entry" $f
1880 setini Favorites profileian.profile "Favorite Profiles" $f
1881 setini ShowMenuBarByDefault false KonsoleWindow $f
1882 setini TabBarPosition Top TabBar $f
1883 }
1884
1885 reset-sakura() {
1886 while read -r k v; do
1887 # shellcheck disable=SC2154
1888 setini $k $v sakura /a/c/subdir_files/.config/sakura/sakura.conf
1889 done <<'EOF'
1890 colorset1_back rgb(33,37,39)
1891 less_questions true
1892 audible_bell No
1893 visible_bell No
1894 disable_numbered_tabswitch true
1895 scroll_lines 10000000
1896 scrollbar true
1897 EOF
1898 }
1899
1900 reset-xscreensaver() {
1901 # except for spash, i set these by setting gui options in
1902 # xscreensaver-command -demo
1903 # then finding the corresponding option in .xscreensaver
1904 # spash, i happened to notice in .xscreensaver
1905 #
1906 # dpmsOff, monitor doesnt come back on using old free software supported nvidia card
1907 cat > /home/iank/.xscreensaver <<'EOF'
1908 mode: blank
1909 dpmsEnabled: True
1910 dpmsStandby: 0:07:00
1911 dpmsSuspend: 0:08:00
1912 dpmsOff: 0:00:00
1913 timeout: 0:05:00
1914 lock: True
1915 lockTimeout: 0:06:00
1916 splash: False
1917 EOF
1918
1919 }
1920
1921
1922 # * stuff that makes sense to be at the end
1923 if [[ "$SUDOD" ]]; then
1924 # allow failure, for example if we are sudoing into a user with diffferent/lesser permissions.
1925 cd "$SUDOD" ||:
1926 unset SUDOD
1927 elif [[ -d /a ]] && [[ $PWD == "$HOME" ]] && [[ $- == *i* ]]; then
1928 cd /a
1929 fi
1930
1931
1932
1933
1934 # for mitmproxy to get a newer python.
1935 # commented until i want to use it because it
1936 # noticably slows bash startup
1937 #
1938
1939 mypyenvinit () {
1940 if [[ $EUID == 0 || ! -e ~/.pyenv/bin ]]; then
1941 echo "error: dont be root. make sure pyenv is installed"
1942 return 1
1943 fi
1944 export PATH="$HOME/.pyenv/bin:$PATH"
1945 eval "$(pyenv init -)"
1946 eval "$(pyenv virtualenv-init -)"
1947 }
1948
1949
1950 export GOPATH=$HOME/go
1951 path-add $GOPATH/bin
1952 path-add /usr/local/go/bin
1953
1954 # I have the git repo and a release. either one should work.
1955 # I have both because I was trying to solve an issue that
1956 # turned out to be unrelated.
1957 # ARDUINO_PATH=/a/opt/Arduino/build/linux/work
1958 export ARDUINO_PATH=/a/opt/arduino-1.8.15
1959 export KALEIDOSCOPE_DIR=/a/opt/Kaleidoscope
1960
1961 # They want to be added to the start, but i think
1962 # that should be avoided unless we really need it.
1963 path-add --end ~/.npm-global
1964
1965
1966 path-add --end $HOME/.cargo/bin
1967
1968 if type -P rg &>/dev/null; then
1969 # --no-messages because of annoying errors on broken symlinks
1970 rg() { command rg --no-messages -L -i -M 300 --no-ignore "$@" || return $?; }
1971 #fails if not exist. ignore
1972 complete -r rg 2>/dev/null ||:
1973 else
1974 alias rg=grr
1975 fi
1976
1977
1978
1979 # taken from default changes to bashrc and bash_profile
1980 path-add --end --ifexists $HOME/.rvm/bin
1981 # also had ruby bin dir, but moved that to environment.sh
1982 # so its included in overall env
1983
1984
1985 export BASEFILE_DIR=/a/bin/fai-basefiles
1986
1987 #export ANDROID_HOME=/a/opt/android-home
1988 # https://f-droid.org/en/docs/Installing_the_Server_and_Repo_Tools/
1989 #export USE_SDK_WRAPPER=yes
1990 #PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools
1991
1992 # didnt get drush working, if I did, this seems like the
1993 # only good thing to include for it.
1994 # Include Drush completion.
1995 # if [ -f "/home/ian/.drush/drush.complete.sh" ] ; then
1996 # source /home/ian/.drush/drush.complete.sh
1997 # fi
1998
1999
2000 # best practice
2001 unset IFS
2002
2003 # https://wiki.archlinux.org/index.php/Xinitrc#Autostart_X_at_login
2004 # i added an extra condition as gentoo xorg guide says depending on
2005 # $DISPLAY is fragile.
2006 if [[ ! $DISPLAY && $XDG_VTNR == 1 ]] && shopt -q login_shell && isarch; then
2007 exec startx
2008 fi
2009
2010
2011 # ensure no bad programs appending to this file will have an affect
2012 return 0