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 ser stop bind9
523 sleep 1
524 sudo rm -fv $f.jnl
525 sudo install -m 644 -o bind -g bind /p/c/machine_specific/vps/bind-initial/db.b8.nz $f
526 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 now=$(date +%s)
977 for (( i=0; i<60; i++ )); do
978 day=$( date +%F -d @$((now - 86400*i )) )
979 date "+%a %b %d" -d @$((now - 86400*i )) | tr '\n' ' '
980 /a/opt/timetrap/bin/t d -ftotal -s $day -e $day all -m '^w|lunch$'
981 done
982 }
983 to() { t out -a "$@"; }
984 ti() { t in -a "$@"; }
985 tl() {
986 to "$*"
987 t s lunch
988 t in -a "$*"
989 m t out -a $(date +%F.%T -d @$(( $(date -d "$(echo $*|sed 's/[_.]/ /g')" +%s) + 60*45 )) )
990 t s w
991 }
992
993 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}' ; }
994
995 idea() {
996 /a/opt/idea-IC-163.7743.44/bin/idea.sh "$@" &r
997 }
998
999 ilogs() {
1000 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"
1001 }
1002
1003 ilog() {
1004 chan=${1:-#fsfsys}
1005 # use * instead of -r since that does sorted order
1006 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
1007 }
1008
1009 o() {
1010 if type gio &> /dev/null ; then
1011 gio open "$@"
1012 elif type gvfs-open &> /dev/null ; then
1013 gvfs-open "$@"
1014 else
1015 xdg-open "$@"
1016 fi
1017 # another alternative is run-mailcap
1018 }
1019 ccomp xdg-open o
1020
1021 # jfilter() {
1022 # grep -Evi -e "^(\S+\s+){4}(sudo|sshd|cron)\[\S*:" \
1023 # -e "^(\S+\s+){4}systemd\[\S*: (starting|started) (btrfsmaintstop|dynamicipupdate|spamd dns bug fix cronjob|rss2email)\.*$"
1024 # }
1025 # jtail() {
1026 # journalctl -n 10000 -f "$@" | jfilter
1027 # }
1028 # jr() { journalctl "$@" | jfilter | less ; }
1029 # jrf() { journalctl -n 200 -f "$@" | jfilter; }
1030
1031 jr() { journalctl "$@" ; }
1032 jrf() { journalctl -n 200 -f "$@" ; }
1033
1034
1035 ccomp journalctl jtail jr jrf
1036
1037 kff() { # keyboardio firmware flash
1038 pushd /a/bin/distro-setup/Arduino/Model01-Firmware
1039 yes $'\n' | make flash
1040 popd
1041 }
1042
1043 wgkey() {
1044 local umask_orig name
1045 if (( $# != 1 )); then
1046 e expected 1 arg >&2
1047 return 1
1048 fi
1049 name=$1
1050 umask_orig=$(umask)
1051 umask 0077
1052 wg genkey | tee $name-priv.key | wg pubkey > $name-pub.key
1053 umask $umask_orig
1054 }
1055 wghole() {
1056 if (( $# != 2 )); then
1057 e expected 2 arg of hostname, ip suffix >&2
1058 return 1
1059 fi
1060 local host ipsuf umask_orig
1061 host=$1
1062 ipsuf=$2
1063 mkdir -p /p/c/machine_specific/$host/filesystem/etc/wireguard
1064 cd /p/c/machine_specific/$host/filesystem/etc/wireguard
1065 umask_orig=$(umask)
1066 umask 0077
1067 wg genkey | tee hole-priv.key | wg pubkey > hole-pub.key
1068 cat >wghole.conf <<EOF
1069 [Interface]
1070 # contents hole-priv.key
1071 PrivateKey = $(cat hole-priv.key)
1072 ListenPort = 1194
1073 Address = 10.8.0.$ipsuf/24
1074 # https://dev.to/tangramvision/what-they-don-t-tell-you-about-setting-up-a-wireguard-vpn-1h2g
1075 # ||: makes the systemd service not fail due to the failed command
1076 PostUp = ping -c1 10.8.0.1 ||:
1077
1078 [Peer]
1079 # li. called wgmail on that server
1080 PublicKey = CTFsje45qLAU44AbX71Vo+xFJ6rt7Cu6+vdMGyWjBjU=
1081 AllowedIPs = 10.8.0.0/24
1082 Endpoint = 72.14.176.105:1194
1083 PersistentKeepalive = 25
1084 EOF
1085 umask $umask_orig
1086 # old approach. systemd seems to work fine and cleaner.
1087 rm -f ../network/interfaces.d/wghole
1088 cedit -q $host /p/c/machine_specific/li/filesystem/etc/wireguard/wgmail.conf <<EOF || [[ $? == 1 ]]
1089 [Peer]
1090 PublicKey = $(cat hole-pub.key)
1091 AllowedIPs = 10.8.0.$ipsuf/32
1092 EOF
1093 cd - >/dev/null
1094 }
1095
1096
1097 lom() {
1098 local l base
1099 if [[ $1 == /* ]]; then
1100 base=${1##*/}
1101 if mountpoint -q /mnt/$base; then
1102 return 0
1103 fi
1104 l=$(losetup -j $1 | sed -rn 's/^([^ ]+): .*/\1/p' | head -n1 ||:)
1105 if [[ ! $l ]]; then
1106 l=$(sudo losetup -f)
1107 m sudo losetup $l $1
1108 fi
1109 if ! sudo cryptsetup status /dev/mapper/$base &>/dev/null; then
1110 if ! sudo cryptsetup luksOpen $l $base; then
1111 m sudo losetup -d $l
1112 return 1
1113 fi
1114 fi
1115 m sudo mkdir -p /mnt/$base
1116 m sudo mount /dev/mapper/$base /mnt/$base
1117 m sudo chown $USER:$USER /mnt/$base
1118 else
1119 base=$1
1120 if mountpoint /mnt/$base &>/dev/null; then
1121 m sudo umount /mnt/$base
1122 fi
1123 if sudo cryptsetup status /dev/mapper/$base &>/dev/null; then
1124 if ! m sudo cryptsetup luksClose /dev/mapper/$base; then
1125 echo lom: failed cryptsetup luksClose /dev/mapper/$base
1126 return 1
1127 fi
1128 fi
1129 l=$(losetup -l --noheadings | awk '$6 ~ /\/'$1'$/ {print $1}')
1130 if [[ $l ]]; then
1131 m sudo losetup -d $l
1132 else
1133 echo lom: warning: no loopback device found
1134 fi
1135 fi
1136 }
1137
1138 # mu personality. for original, just run mp. for 2, run mp 2.
1139 # this is partly duplicated in mail-setup
1140 mp() {
1141 local dead=false
1142 for s in {1..5}; do
1143 if ! killall mu; then
1144 dead=true
1145 break
1146 fi
1147 sleep 1
1148 done
1149 if ! $dead; then
1150 echo error: mu not dead
1151 m psg mu
1152 return 1
1153 fi
1154 suf=$1
1155 set -- /m/mucache ~/.cache/mu /m/.mu ~/.config/mu
1156 while (($#)); do
1157 target=$1$suf
1158 f=$2
1159 shift 2
1160 if [[ -e $f && ! -L $f ]]; then
1161 m rm -rf $f
1162 fi
1163 m ln -sf -T $target $f
1164 done
1165 }
1166
1167 # these might need a mu index or something added.
1168 mbenable() {
1169 local mb=$1
1170 dst=/m/4e/$mb
1171 src=/m/md/$mb
1172 [[ -e $src ]] || { echo "src:$src does not exist"; return 1; }
1173 m mv -T $src $dst
1174 m ln -s -T $dst $src
1175 }
1176 mb2enable() {
1177 local mb
1178 for mb; do
1179 dst=/m/4e2/$mb
1180 link=/m/md/$mb
1181 src=/m/md/$mb
1182 if [[ ! -e $src || -L $src ]]; then
1183 src=/m/4e/$mb
1184 fi
1185 [[ -e $src ]] || { echo "src:$src does not exist"; return 1; }
1186 m mv -T $src $dst
1187 m ln -sf -T $dst $link
1188 done
1189 }
1190 mbdisable() {
1191 local mb=$1
1192 dst=/m/md/$mb
1193 src=/m/4e/$mb
1194 set -x
1195 [[ -e $src ]] || { set +x; return 1; }
1196 if [[ -L $dst ]]; then rm $dst; fi
1197 mv -T $src $dst
1198 set +x
1199 }
1200
1201
1202 mdt() {
1203 markdown "$1" >/tmp/mdtest.html
1204 firefox /tmp/mdtest.html
1205 }
1206
1207 mo() { xset dpms force off; } # monitor off
1208
1209 mpvd() {
1210 mpv --profile=d "$@";
1211 }
1212 mpvs() {
1213 mpv --profile=s "$@";
1214 }
1215
1216 myirc() {
1217 if [[ ! $1 ]]; then
1218 set -- fsf-office
1219 fi
1220 local d1 d2
1221 d=( /var/lib/znc/moddata/log/iank/{freenode,libera} )
1222 # use * instead of -r since that does sorted order
1223 ssh root@iankelling.org "for f in ${d[@]}; do cd \$f/#$1; grep '\<iank.*' *; done" | cut --complement -c12-16
1224 }
1225 mypidgin() {
1226 c /p/c/.purple/logs/jabber/iank@fsf.org/office@conference.fsf.org.chat
1227 for x in *.html; do html2text -o ${x%.html}.txt $x; done;
1228 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}'
1229 }
1230 allmyirc() {
1231 local d
1232 d=/var/lib/znc/moddata/log/iank/freenode
1233 ssh root@iankelling.org "cd $d; find . -mtime -60 -type f -exec grep '\<iank.*' {} +" | sed -r 's,^..([^/]*)/(.{11})(.{5})(.{8}).,\2\4 \1,' | sort
1234 }
1235
1236 mygajim() {
1237 local now time time_sec time_pretty
1238 now=$(date +%s)
1239 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
1240 case $time in
1241 16*) : ;;
1242 *) continue ;;
1243 esac
1244 if ! time_pretty=$(date +%F.%R -d @$time); then
1245 echo bad time: $time
1246 return 1
1247 fi
1248 echo $time_pretty "$l"
1249 time_sec=${time%%.*}
1250 # only look at the last 18 days. generally just use this for timesheet.
1251 if (( time_sec < now - 60 * 60 * 24 * 18 )); then break; fi
1252 done
1253 }
1254
1255 gajlogs() {
1256 sqlite3 -separator ' ' /p/c/subdir_files/.local/share/gajim/logs.db "select time, message from logs" | less
1257 }
1258
1259 net-dev-info() {
1260 e "lspci -nnk|gr -iA2 net"
1261 lspci -nnk|gr -iA2 net
1262 hr
1263 e "s lshw -C network"
1264 hr
1265 sudo lshw -C network
1266 }
1267
1268 nk() {
1269 ser stop NetworkManager
1270 ser disable NetworkManager
1271 ser stop NetworkManager-wait-online.service
1272 ser disable NetworkManager-wait-online.service
1273 ser stop dnsmasq
1274 sudo resolvconf -d NetworkManager
1275 # ser start dnsmasq
1276 sudo ifup br0
1277 }
1278 ngo() {
1279 sudo ifdown br0
1280 ser start NetworkManager
1281 sleep 4
1282 sudo nmtui-connect
1283 }
1284
1285 otp() {
1286 oathtool --totp -b "$*" | xclip -selection clipboard
1287 }
1288 j() {
1289 "$@" |& pee "xclip -r -selection clipboard"
1290 }
1291
1292
1293 pakaraoke() {
1294 # from http://askubuntu.com/questions/456021/remove-vocals-from-mp3-and-get-only-instrumentals
1295 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
1296 }
1297
1298 pfind() { #find *$1* in $PATH
1299 [[ $# != 1 ]] && { echo requires 1 argument; return 1; }
1300 local pathArray
1301 IFS=: pathArray=($PATH); unset IFS
1302 find "${pathArray[@]}" -iname "*$1*"
1303 }
1304
1305 pick-trash() {
1306 # trash-restore lists everything that has been trashed at or below CWD
1307 # This picks out files just in CWD, not subdirectories,
1308 # which also match grep $1, usually use $1 for a time string
1309 # which you get from running restore-trash once first
1310 local name x ask
1311 local nth=1
1312 # last condition is to not ask again for ones we skipped
1313 while name="$( echo | restore-trash | gr "$PWD/[^/]\+$" | gr "$1" )" \
1314 && [[ $name ]] && (( $(wc -l <<<"$name") >= nth )); do
1315 name="$(echo "$name" | head -n $nth | tail -n 1 )"
1316 read -r -p "$name [Y/n] " ask
1317 if [[ ! $ask || $ask == [Yy] ]]; then
1318 x=$( echo "$name" | gr -o "^\s*[0-9]*" )
1319 echo $x | restore-trash > /dev/null
1320 elif [[ $ask == [Nn] ]]; then
1321 nth=$((nth+1))
1322 else
1323 return
1324 fi
1325 done
1326 }
1327
1328
1329 pub() {
1330 rld /a/h/_site/ li:/var/www/iankelling.org/html
1331 }
1332
1333
1334 pumpa() {
1335 # fixes the menu bar in xmonad. this won\'t be needed when xmonad
1336 # packages catches up on some changes in future (this is written in
1337 # 4/2017)
1338 #
1339 # geekosaur: so youll want to upgrade to xmonad 0.13 or else use a
1340 # locally modified XMonad.Hooks.ManageDocks that doesnt set the
1341 # work area; turns out it\'s impossible to set correctly if you are
1342 # not a fully EWMH compliant desktop environment
1343 #
1344 # geekosaur: chrome shows one failure mode, qt/kde another, other
1345 # gtk apps a third, ... I came up with a setting that works for me
1346 # locally but apparently doesnt work for others, so we joined the
1347 # other tiling window managers in giving up on setting it at all
1348 #
1349 xprop -root -remove _NET_WORKAREA
1350 command pumpa & r
1351 }
1352
1353 # reviewboard, used at my old job
1354 #rbpipe() { rbt post -o --diff-filename=- "$@"; }
1355 #rbp() { rbt post -o "$@"; }
1356
1357 rebr() {
1358 sudo ifdown br0
1359 sudo ifup br0
1360 }
1361
1362
1363 # only run on MAIL_HOST. simpler to keep this on one system.
1364 r2eadd() { # usage: name url
1365 # initial setup of rss2email:
1366 # r2e new r2e@iankelling.org
1367 # that initializes files, and sets default email.
1368 # symlink to the config doesnt work, so I copied it to /p/c
1369 # and then use cli option to specify explicit path.
1370 # Only option changed from default config is to set
1371 # force-from = True
1372 #
1373 # or else for a few feeds, the from address is set by the feed, and
1374 # if I fail delivery, then I send a bounce message to that from
1375 # address, which makes me be a spammer.
1376
1377 r2e add $1 "$2" $1@r2e.iankelling.org
1378 # get up to date and dont send old entries now:
1379 r2e run --no-send $1
1380 }
1381 r2e() { command r2e -d /p/c/rss2email.json -c /p/c/rss2email.cfg "$@"; }
1382
1383 rspicy() { # usage: HOST DOMAIN
1384 # connect to spice vm remote host. use vspicy for local host
1385 local port
1386 # shellcheck disable=SC2087
1387 port=$(ssh $1<<EOF
1388 sudo virsh dumpxml $2|grep "<graphics.*type='spice'" | \
1389 sed -rn "s/.*port='([0-9]+).*/\1/p"
1390 EOF
1391 )
1392 if [[ $port ]]; then
1393 spicy -h $1 -p $port
1394 else
1395 echo "error: no port found. check that the domain is running."
1396 fi
1397 }
1398
1399
1400 scssl() {
1401 # s gem install scss-lint
1402 pushd /a/opt/thoughtbot-guides
1403 git pull --stat
1404 popd
1405 scss-lint -c /a/opt/thoughtbot-guides/style/sass/.scss-lint.yml "$@"
1406 }
1407
1408 skbrc() {
1409 sk -e 2120,245 /b/ds/brc /b/ds/brc2
1410 }
1411
1412 skaraoke() {
1413 local tmp out
1414 out=${2:-${1%.*}.sh}
1415 tmp=$(mktemp -d)
1416 script -t -c "mpv --no-config --no-resume-playback --no-terminal --no-audio-display '$1'" $tmp/typescript 2>$tmp/timing
1417 # todo, the current sleep seems pretty good, but it
1418 # would be nice to have an empirical measurement, or
1419 # some better wait to sync up.
1420 #
1421 # note: --loop-file=no prevents it from hanging if you have that
1422 # set to inf the mpv config.
1423 # --loop=no prevents it from exit code 3 due to stdin if you
1424 # had it set to inf in mpv config.
1425 #
1426 # args go to mpv, for example --volume=80, 50%
1427 cat >$out <<EOFOUTER
1428 #!/bin/bash
1429 trap "trap - TERM && kill 0" INT TERM ERR; set -e
1430 ( sleep .2; scriptreplay <( cat <<'EOF'
1431 $(cat $tmp/timing)
1432 EOF
1433 ) <( cat <<'EOF'
1434 $(cat $tmp/typescript)
1435 EOF
1436 ))&
1437 base64 -d - <<'EOF'| mpv --loop=no --loop-file=no --no-terminal --no-audio-display "\$@" -
1438 $(base64 "$1")
1439 EOF
1440 kill 0
1441 EOFOUTER
1442 rm -r $tmp
1443 chmod +x $out
1444 }
1445
1446 smeld() { # ssh meld usage host1 host2 file
1447 meld <(ssh $1 cat $3) <(ssh $2 cat $3)
1448 }
1449
1450 spd() {
1451 PATH=/usr/local/spdhackfix:$PATH command spd "$@"
1452 }
1453
1454 spend() {
1455 sudo systemctl suspend
1456 }
1457
1458 spamf() { # spamtest on FILE
1459 local spamcpre spamdpid
1460
1461 if (( $# != 1 )); then
1462 e spamtest error: expected 1 arg, filename >&2
1463 return 1
1464 fi
1465
1466 spamdpid=$(systemctl status spamassassin| sed -n '/^ *Main PID:/s/[^0-9]//gp')
1467 spamcpre="nsenter -t $spamdpid -n -m"
1468 s $spamcpre sudo -u Debian-exim spamassassin -t --cf='score PYZOR_CHECK 0' <"$1"
1469 }
1470
1471
1472 # mail related
1473 testmail() {
1474 declare -gi _seq; _seq+=1
1475 echo "test body" | m mail -s "test mail from $HOSTNAME, $_seq" "${@:-root@localhost}"
1476 # for testing to send from an external address, you can do for example
1477 # -fian@iank.bid -aFrom:ian@iank.bid web-6fnbs@mail-tester.com
1478 # note in exim, you can retry a deferred message
1479 # s exim -M MSG_ID
1480 # MSG_ID is in /var/log/exim4/mainlog, looks like 1ccdnD-0001nh-EN
1481 }
1482
1483 # to test sieve, use below command. for fsf mail, see offlineimap-sync script
1484 # make modifications, then copy to live file, use -eW to actually modify mailbox
1485 #
1486 # Another option is to use sieve-test SCRIPT MAIL_FILE. note,
1487 # sieve-test doesnt know about envelopes, Im not sure if sieve-filter does.
1488
1489 # sieve with output filter. arg is mailbox, like INBOX.
1490 # This depends on dovecot conf, notably mail_location in /etc/dovecot/conf.d/10-mail.conf
1491
1492 # always run this first, edit the test files, then run the following
1493 testsieve() {
1494 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
1495 }
1496 runsieve() {
1497 c ~/sieve; cp personal{test,}.sieve; cp lists{test,}.sieve; cp personalend{test,}.sieve
1498 sieve-filter -eWv ~/sieve/maintest.sieve ${1:-INBOX} delete &> /tmp/testsieve.log
1499 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
1500 }
1501
1502 # usage:
1503 # alertme SUBJECT
1504 # printf "subject\nbody\n" | alertme
1505 alertme() {
1506 if [[ -t 0 ]]; then
1507 exim -t <<EOF
1508 From: alertme@b8.nz
1509 To: alerts@iankelling.org
1510 Subject: $*
1511 EOF
1512 else
1513 read sub
1514 { cat <<EOF
1515 From: alertme@b8.nz
1516 To: alerts@iankelling.org
1517 Subject: $sub
1518
1519 EOF
1520 cat
1521 } | exim -t
1522 fi
1523 }
1524 daylertme() {
1525 if [[ -t 0 ]]; then
1526 exim -t <<EOF
1527 From: alertme@b8.nz
1528 To: daylert@iankelling.org
1529 Subject: $*
1530 EOF
1531 else
1532 read sub
1533 { cat <<EOF
1534 From: alertme@b8.nz
1535 To: daylert@iankelling.org
1536 Subject: $sub
1537
1538 EOF
1539 cat
1540 } | exim -t
1541 fi
1542 }
1543
1544 # alert when a page goes live. not urgent.
1545 alert200() {
1546 url="$1"
1547 tmpdir="$(mktemp -d)"
1548 cd $tmpdir
1549 while true; do
1550 if torsocks wget -q "$url"; then
1551 alertme $tmpdir
1552 fi
1553 sleep $(( 600 + RANDOM % 300 ))
1554 done
1555 }
1556
1557
1558 # mail related
1559 testexim() {
1560 # testmail above calls sendmail, which is a link to exim/postfix.
1561 # its docs dont say a way of adding an argument
1562 # to sendmail to turn on debug output. We could make a wrapper, but
1563 # that is a pain. Exim debug args are documented here:
1564 # http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
1565 #
1566 # http://www.exim.org/exim-html-current/doc/html/spec_html/ch-building_and_installing_exim.html
1567 # note, for exim daemon, you can turn on debug options by
1568 # adding -d, etc to COMMONOPTIONS in
1569 # /etc/default/exim4
1570 #
1571 # to specify recipients other than those in to, cc, bcc, you can use the cli args, eg:
1572 # exim -t 'test@zroe.org, t2@zroe.org' <<'EOF'
1573 #
1574 # -t = get recipient from header
1575 exim -d -t <<'EOF'
1576 From: i@dmarctest.b8.nz
1577 To: mailman@dev.fsf.org
1578 Subject: test2
1579 Reply-to: rtest@iankelling.org
1580
1581 This is a test message.
1582 EOF
1583 }
1584
1585 # toggle keyboard
1586 tk() {
1587 # based on
1588 # https://askubuntu.com/questions/160945/is-there-a-way-to-disable-a-laptops-internal-keyboard
1589 id=$(xinput --list --id-only 'AT Translated Set 2 keyboard')
1590 if xinput list | grep -F '∼ AT Translated Set 2 keyboard' &>/dev/null; then
1591 echo enabling keyboard
1592 # find the first slave keyboard number, they are all the same in my output.
1593 # if they werent, worst case we would need to save the slave number somewhere
1594 # when it got disabled.
1595 slave=$(xinput list | sed -n 's/.*slave \+keyboard (\([0-9]*\)).*/\1/p' | head -n1)
1596 xinput reattach $id $slave
1597 else
1598 xinput float $id
1599 fi
1600 }
1601
1602 tm() {
1603 # timer in minutes
1604 # --no-config
1605 (sleep $(calc "$* * 60") && mpv --no-config --volume 50 /a/bin/data/alarm.mp3) > /dev/null 2>&1 &
1606 }
1607
1608 trg() { transmission-remote-gtk & r; }
1609 trc() {
1610 # example, set global upload limit to 100 kilobytes:
1611 # trc -u 100
1612 TR_AUTH=":$(jq -r .profiles[0].password ~/.config/transmission-remote-gtk/config.json)" transmission-remote transmission.lan -ne "$@"
1613 }
1614
1615 trysleep() {
1616 retries="$1"
1617 sleepsecs="$2"
1618 shift 2
1619 for (( i=0; i < retries - 1; i++ )); do
1620 if "$@"; then
1621 return 0
1622 fi
1623 sleep $sleepsecs
1624 done
1625 "$@"
1626 }
1627
1628
1629 tu() {
1630 local s
1631 if [[ -e $1 && ! -w $1 || ! -w $(dirname "$1") ]]; then
1632 s=s;
1633 fi
1634 # full path for using in some initial setup steps
1635 $s /a/exe/teeu "$@"
1636 }
1637
1638 enn() {
1639 local ecmd pid
1640
1641 ecmd="/usr/sbin/exim4 -C /etc/exim4/my.conf"
1642 if ip a show veth1-mail &>/dev/null; then
1643 s $ecmd "$@"
1644 return
1645 fi
1646 pid=$(pgrep -f "/usr/sbin/exim4 -bd -q30m -C /etc/exim4/my.conf"|h1)
1647 m s nsenter -t $pid -n -m $ecmd "$@"
1648 }
1649
1650 sdnbash() { # systemd namespace bash
1651 local unit=$1
1652 m sudo nsenter -t $(systemctl show --property MainPID --value $unit) -n -m sudo -u $USER -i bash
1653 }
1654
1655 mailnnbash() {
1656 m sudo nsenter -t $(systemctl show --property MainPID --value mailnn) -n -m sudo -u $USER -i bash
1657 }
1658
1659 mailvpnbash() {
1660 m sudo nsenter -t $(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/.*mail.conf") -n -m sudo -u $USER -i bash
1661 }
1662 eximbash() {
1663 local pid
1664 pid=$(pgrep -f "/usr/sbin/exim4 -bd -q30m -C /etc/exim4/my.conf"|h1)
1665 if [[ ! $pid ]]; then
1666 echo "eximbash: failed to find exim pid. systemctl -n 30 status exim4:"
1667 systemctl status exim4
1668 fi
1669 m sudo nsenter -t $pid -n -m
1670 }
1671 spamnn() {
1672 local spamdpid
1673 spamdpid=$(systemctl show --property MainPID --value spamassassin)
1674 m sudo nsenter -t $spamdpid -n -m sudo -u Debian-exim spamassassin "$@"
1675 }
1676 unboundbash() {
1677 m sudo nsenter -t $(systemctl status unbound| sed -n '/^ *Main PID:/s/[^0-9]//gp') -n -m sudo -u $USER -i bash
1678 }
1679
1680 mailnncheck() {
1681 local p pid ns mailnn
1682 # mailvpn would belong on the list if using openvpn
1683 for p in mailnn unbound dovecot spamassassin exim4 radicale; do
1684 case $p in
1685 exim4|radicale)
1686 pid=$(ps -eo pid,cgroup | grep /system.slice/$p.service | awk '{print $1}')
1687 ;;
1688 *)
1689 pid=$(s systemctl show --property MainPID --value $p)
1690 ;;
1691 esac
1692 echo p=$p pid=$pid
1693 if [[ ! $pid ]]; then
1694 echo failed to find pid for $p
1695 continue
1696 fi
1697 if ! ns=$(s readlink /proc/$pid/ns/net); then
1698 echo failed to find ns for $p pid=$pid
1699 continue
1700 fi
1701 if [[ $mailnn ]]; then
1702 if [[ $ns != "$mailnn" ]]; then
1703 echo "$p ns $ns != $mailnn"
1704 fi
1705 else
1706 mailnn=$ns
1707 fi
1708 done
1709
1710 }
1711
1712
1713 vpncmd() {
1714 m sudo -E env "PATH=$PATH" nsenter -t $(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/.*client.conf") -n -m "$@"
1715 }
1716 vpnf() {
1717 vpncmd sudo -E -u iank env "PATH=$PATH" abrowser -no-remote -P vpn & r
1718 }
1719 vpn2f() {
1720 vpncmd sudo -u iank env "PATH=$PATH" abrowser -no-remote -P vpn2 & r
1721 }
1722
1723 vpni() {
1724 vpncmd sudo -u iank env "PATH=$PATH" "$@"
1725 }
1726 vpnbash() {
1727 vpncmd bash
1728 }
1729
1730
1731 vpn() {
1732 if [[ -e /lib/systemd/system/openvpn-client@.service ]]; then
1733 local vpn_service=openvpn-client
1734 else
1735 local vpn_service=openvpn
1736 fi
1737
1738 [[ $1 ]] || { echo need arg; return 1; }
1739 journalctl --unit=$vpn_service@$1 -f -n0 &
1740 # sometimes the journal doesnt open until after the vpn output
1741 # has happened. hoping this fixes that.
1742 sleep 1
1743 sudo systemctl start $vpn_service@$1
1744 # sometimes the ask-password agent does not work and needs a delay.
1745 sleep .5
1746 # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=779240
1747 # noticed around 8-2017 after update from around stretch release
1748 # on debian testing, even though the bug is much older.
1749 sudo systemd-tty-ask-password-agent
1750 }
1751
1752 fixu() {
1753 ls -lad /run/user/1000
1754 s chmod 700 /run/user/1000; s chown iank.iank /run/user/1000
1755 }
1756
1757 # systemctl is-enabled / status / cat says nothing, instead theres
1758 # some obscure symlink. paths copied from man systemd.unit.
1759 # possibly also usefull, but incomplete, doesnt show units not loaded in memory:
1760 # seru list-dependencies --reverse --all UNIT
1761 sysd-deps() {
1762 local f
1763 local -a dirs search
1764 ngset
1765
1766 case $1 in
1767 u)
1768 search=(
1769 ~/.config/systemd/user.control/*
1770 $XDG_RUNTIME_DIR/systemd/user.control/*
1771 $XDG_RUNTIME_DIR/systemd/transient/*
1772 $XDG_RUNTIME_DIR/systemd/generator.early/*
1773 ~/.config/systemd/user/*
1774 /etc/systemd/user/*
1775 $XDG_RUNTIME_DIR/systemd/user/*
1776 /run/systemd/user/*
1777 $XDG_RUNTIME_DIR/systemd/generator/*
1778 ~/.local/share/systemd/user/*
1779 /usr/lib/systemd/user/*
1780 $XDG_RUNTIME_DIR/systemd/generator.late/*
1781 )
1782 ;;
1783 *)
1784 search=(
1785 /etc/systemd/system.control/*
1786 /run/systemd/system.control/*
1787 /run/systemd/transient/*
1788 /run/systemd/generator.early/*
1789 /etc/systemd/system/*
1790 /etc/systemd/systemd.attached/*
1791 /run/systemd/system/*
1792 /run/systemd/systemd.attached/*
1793 /run/systemd/generator/*
1794 /lib/systemd/system/*
1795 /run/systemd/generator.late/*
1796 )
1797 ;;
1798 esac
1799 for f in "${search[@]}"; do
1800 [[ -d $f ]] || continue
1801 case $f in
1802 *.requires|*.wants)
1803 dirs+=("$f")
1804 ;;
1805 esac
1806 done
1807 # dirs is just so we write out the directory names, ls does it when there is 2 or more dirs.
1808 case ${#dirs[@]} in
1809 1)
1810 echo "${dirs[0]}:"
1811 ll "${dirs[@]}"
1812 ;;
1813 0) : ;;
1814 *)
1815 ll "${dirs[@]}"
1816 ;;
1817 esac
1818 ngreset
1819 }
1820
1821 fixvpndns() {
1822 local link istls
1823 read _ link _ istls < <(resolvectl dnsovertls tunfsf)
1824 case $istls in
1825 yes|no) : ;;
1826 *) echo fixvpndns error: unexpected istls value: $istls >&2; return 1 ;;
1827 esac
1828 s busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager SetLinkDNSOverTLS is $link no
1829 }
1830
1831 vpnoff() {
1832 [[ $1 ]] || { echo need arg; return 1; }
1833 if [[ -e /lib/systemd/system/openvpn-client@.service ]]; then
1834 local vpn_service=openvpn-client
1835 else
1836 local vpn_service=openvpn
1837 fi
1838 sudo systemctl stop $vpn_service@$1
1839 }
1840 vpnoffc() { # vpn off client
1841 ser stop openvpn-client-tr@client
1842 }
1843 vpnc() {
1844 ser start openvpn-client-tr@client
1845 }
1846
1847
1848 vspicy() { # usage: VIRSH_DOMAIN
1849 # connect to vms made with virt-install
1850 spicy -p $(sudo virsh dumpxml "$1"|grep "<graphics.*type='spice'"|\
1851 sed -r "s/.*port='([0-9]+).*/\1/")
1852 }
1853
1854 wian() {
1855 cat-new-files /m/4e/INBOX/new
1856 }
1857
1858 wtr() { curl wttr.in/boston; }
1859
1860 xevkb() { xev -event keyboard; }
1861
1862 # * misc stuff
1863
1864 vrun() {
1865 printf "running: %s\n" "$*"
1866 "$@"
1867 }
1868
1869 f=/a/f/ansible-configs/files/common/etc/fsf-workstation-bashrc.sh
1870 if [[ -e $f ]]; then
1871 # shellcheck disable=SC1090
1872 source $f
1873 fi
1874
1875
1876
1877
1878 reset-konsole() {
1879 # we also have a file in /a/c/...konsole...
1880 local f=$HOME/.config/konsolerc
1881 setini DefaultProfile profileian.profile "Desktop Entry" $f
1882 setini Favorites profileian.profile "Favorite Profiles" $f
1883 setini ShowMenuBarByDefault false KonsoleWindow $f
1884 setini TabBarPosition Top TabBar $f
1885 }
1886
1887 reset-sakura() {
1888 while read -r k v; do
1889 # shellcheck disable=SC2154
1890 setini $k $v sakura /a/c/subdir_files/.config/sakura/sakura.conf
1891 done <<'EOF'
1892 colorset1_back rgb(33,37,39)
1893 less_questions true
1894 audible_bell No
1895 visible_bell No
1896 disable_numbered_tabswitch true
1897 scroll_lines 10000000
1898 scrollbar true
1899 EOF
1900 }
1901
1902 reset-xscreensaver() {
1903 # except for spash, i set these by setting gui options in
1904 # xscreensaver-command -demo
1905 # then finding the corresponding option in .xscreensaver
1906 # spash, i happened to notice in .xscreensaver
1907 #
1908 # dpmsOff, monitor doesnt come back on using old free software supported nvidia card
1909 cat > /home/iank/.xscreensaver <<'EOF'
1910 mode: blank
1911 dpmsEnabled: True
1912 dpmsStandby: 0:07:00
1913 dpmsSuspend: 0:08:00
1914 dpmsOff: 0:00:00
1915 timeout: 0:05:00
1916 lock: True
1917 lockTimeout: 0:06:00
1918 splash: False
1919 EOF
1920
1921 }
1922
1923
1924 # * stuff that makes sense to be at the end
1925 if [[ "$SUDOD" ]]; then
1926 # allow failure, for example if we are sudoing into a user with diffferent/lesser permissions.
1927 cd "$SUDOD" ||:
1928 unset SUDOD
1929 elif [[ -d /a ]] && [[ $PWD == "$HOME" ]] && [[ $- == *i* ]]; then
1930 cd /a
1931 fi
1932
1933
1934
1935
1936 # for mitmproxy to get a newer python.
1937 # commented until i want to use it because it
1938 # noticably slows bash startup
1939 #
1940
1941 mypyenvinit () {
1942 if [[ $EUID == 0 || ! -e ~/.pyenv/bin ]]; then
1943 echo "error: dont be root. make sure pyenv is installed"
1944 return 1
1945 fi
1946 export PATH="$HOME/.pyenv/bin:$PATH"
1947 eval "$(pyenv init -)"
1948 eval "$(pyenv virtualenv-init -)"
1949 }
1950
1951
1952 export GOPATH=$HOME/go
1953 path-add $GOPATH/bin
1954 path-add /usr/local/go/bin
1955
1956 # I have the git repo and a release. either one should work.
1957 # I have both because I was trying to solve an issue that
1958 # turned out to be unrelated.
1959 # ARDUINO_PATH=/a/opt/Arduino/build/linux/work
1960 export ARDUINO_PATH=/a/opt/arduino-1.8.15
1961 export KALEIDOSCOPE_DIR=/a/opt/Kaleidoscope
1962
1963 # They want to be added to the start, but i think
1964 # that should be avoided unless we really need it.
1965 path-add --end ~/.npm-global
1966
1967
1968 path-add --end $HOME/.cargo/bin
1969
1970 if type -P rg &>/dev/null; then
1971 # --no-messages because of annoying errors on broken symlinks
1972 rg() { command rg --no-messages -L -i -M 300 --no-ignore "$@" || return $?; }
1973 #fails if not exist. ignore
1974 complete -r rg 2>/dev/null ||:
1975 else
1976 alias rg=grr
1977 fi
1978
1979
1980
1981 # taken from default changes to bashrc and bash_profile
1982 path-add --end --ifexists $HOME/.rvm/bin
1983 # also had ruby bin dir, but moved that to environment.sh
1984 # so its included in overall env
1985
1986
1987 export BASEFILE_DIR=/a/bin/fai-basefiles
1988
1989 #export ANDROID_HOME=/a/opt/android-home
1990 # https://f-droid.org/en/docs/Installing_the_Server_and_Repo_Tools/
1991 #export USE_SDK_WRAPPER=yes
1992 #PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools
1993
1994 # didnt get drush working, if I did, this seems like the
1995 # only good thing to include for it.
1996 # Include Drush completion.
1997 # if [ -f "/home/ian/.drush/drush.complete.sh" ] ; then
1998 # source /home/ian/.drush/drush.complete.sh
1999 # fi
2000
2001
2002 # best practice
2003 unset IFS
2004
2005 # https://wiki.archlinux.org/index.php/Xinitrc#Autostart_X_at_login
2006 # i added an extra condition as gentoo xorg guide says depending on
2007 # $DISPLAY is fragile.
2008 if [[ ! $DISPLAY && $XDG_VTNR == 1 ]] && shopt -q login_shell && isarch; then
2009 exec startx
2010 fi
2011
2012
2013 # ensure no bad programs appending to this file will have an affect
2014 return 0