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