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