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