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