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