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