mostly new music stuff
[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 if [[ $LESSHISTFILE == - ]]; then
10 HISTFILE=
11 c() { cd "$@"; }
12 elif [[ $HISTFILE ]]; then
13 HISTFILE=$HOME/.bh
14 fi
15
16 source /a/bin/distro-setup/path-add-function
17 path-add /a/exe
18 # add this with absolute paths as needed for better security
19 #path-add --end /path/to/node_modules/.bin
20 ## for yarn, etc
21 #path-add --end /usr/lib/node_modules/corepack/shims/
22
23 # pip3 --user things go here:
24 path-add --end ~/.local/bin
25 path-add --ifexists --end /a/work/libremanage
26 path-add --ifexists --end /a/opt/adt-bundle*/tools /a/opt/adt-bundle*/platform-tools
27 path-add --ifexists --end /a/opt/scancode-toolkit-3.10.
28 path-add --ifexists --end /p/bin
29
30 case $HOSTNAME in
31 sy|bo)
32 # https://askubuntu.com/questions/1254544/vlc-crashes-when-opening-any-file-ubuntu-20-04
33 if grep -qE '^VERSION_CODENAME="(nabia|focal)"' /etc/os-release &>/dev/null; then
34 export MESA_LOADER_DRIVER_OVERRIDE=i965
35 fi
36 ;;
37 esac
38
39
40 export WCDHOME=/a
41
42
43 case $EUID in
44 0)
45 SL_SSH_ARGS="-F $HOME/.ssh/confighome"
46 ;;
47 esac
48
49
50 # * include files
51
52 # generated instead of dynamic for the benefit of shellcheck
53 #for x in /a/bin/distro-functions/src/* /a/bin/!(githtml)/*-function?(s); do echo source $x ; done
54 source /a/bin/distro-functions/src/identify-distros
55 source /a/bin/log-quiet/logq-function
56 # for x in /a/bin/bash_unpublished/source-!(.#*); do echo source $x; done
57 source /a/bin/bash_unpublished/source-semi-priv
58 source /a/bin/bash_unpublished/source-state
59
60 source /a/bin/log-quiet/logq-function
61 if [[ -s /a/opt/alacritty/extra/completions/alacritty.bash ]]; then
62 source /a/opt/alacritty/extra/completions/alacritty.bash
63 fi
64
65
66 # * functions
67
68 multimic() {
69 local i
70 local -a sources
71
72 m pactl unload-module module-loopback
73 m pactl unload-module module-null-sink
74 m pactl unload-module module-remap-source
75
76 sources=($(pacmd list-sources | sed -rn 's/.*name: <([^>]+).*/\1/p'))
77
78 if (( ! $# )); then
79 i=0
80 for s in ${sources[@]}; do
81 e $i $s
82 i=$(( i+1 ))
83 done
84 read -r l
85 set -- $l
86 fi
87 m pactl load-module module-null-sink sink_name=ianinput sink_properties=device.description=ianinputs
88 for i; do
89 m pactl load-module module-loopback source=${sources[i]} sink_dont_move=true sink=ianinput
90 done
91 pactl load-module module-remap-source source_name=iancombine master=ianinput.monitor source_properties=device.description=iancombine
92 }
93
94 # h ssh test
95 # For testing restrictive ssh.
96 hstest() {
97 install-my-scripts
98 d=$(mktemp -d)
99 sed '/^ *IdentityFile/d' ~/.ssh/config >$d/config
100 s command ssh -F $d/config -i /q/root/h "$@"
101 }
102
103 # h rsync test
104 # For testing restrictive rsync
105 hrtest() { #
106 install-my-scripts
107 d=$(mktemp -d)
108 sed '/^ *IdentityFile/d' ~/.ssh/config >$d/config
109 s rsync -e "ssh -F $d/config -i /q/root/h" "$@"
110 }
111
112 # rsync as root and avoid the default restrictive h key & config.
113 rootrsync() {
114 s rsync -e "ssh -F /root/.ssh/confighome" "$@"
115 }
116
117 zcheck() {
118 ssh bow DISPLAY=:0 scrot /tmp/oegu.jpg
119 scp bow:/tmp/oegu.jpg /t
120 ssh bow rm /tmp/oegu.jpg
121 feh /t/oegu.jpg
122 }
123
124 slemacs() {
125 local arg rtime v
126 arg="$1"
127 remote="$2"
128 if [[ $arg == [89]0Etiona* ]]; then
129 v=${arg::1}
130 rtime=${arg#*Etiona} # remote time
131 if [[ ! $rtime ]]; then
132 rtime=0
133 fi
134 dir=/a/opt/emacs-trisquel${v}-nox/.iank
135 ltime=$(stat -c%Y $dir/e/e/.emacs.d/init.el)
136 if (( ltime > rtime )); then
137 m rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" $dir "$remote":/home/iank
138 fi
139 fi
140 }
141
142 sle() { # sl emacs
143 local f=/home/iank/.emacs.d/init.el
144 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 "$@"
145 }
146 ccomp ssh sle
147
148 # Run this manually after .emacs.d changes. Otherwise, to check if
149 # files changed with find takes 90ms. sl normally only adds 25ms. We
150 # could cut it down to 10ms if we put things on a btrfs filesystem and
151 # looked for changes there, or used some inotify thing, but that seems
152 # like too much work.
153 egh() { # emacs gnuhope
154 RSYNC_RSH=ssh m rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" /a/opt/emacs-trisquel9-nox/.iank lists2d.fsf.org:.ianktrisquel_9
155 RSYNC_RSH=ssh m rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" /a/opt/emacs-trisquel8-nox/.iank lists2d.fsf.org:/home/iank
156 }
157 ekw() {
158 local shell="bash -s"
159 if [[ $HOSTNAME != kw ]]; then
160 shell="ssh kw.office.fsf.org"
161 bbk -m /a -t kw
162 fi
163 $shell <<'EOF'
164 sudo mkdir /root/.ianktrisquel_9
165 sudo rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" /a/opt/emacs-trisquel9-nox/.iank /root/.ianktrisquel_9
166 rsync -rptL --delete --filter=". /b/ds/sl/rsync-filter" /a/opt/emacs-trisquel8-nox/.iank /home/iank
167 EOF
168 }
169
170 rm-docker-iptables() {
171 s iptables -S | gr docker | gr -- -A | sed 's/-A/-D/'| while read -r l; do sudo iptables $l; done
172 s iptables -S -t nat | gr docker | gr -- -A | sed 's/-A/-D/'| while read -r l; do sudo iptables -t nat $l; done
173 s iptables -S | gr docker | gr -- -N | sed 's/-N/-X/'| while read -r l; do sudo iptables $l; done
174 s iptables -S -t nat | gr docker | gr -- -N | sed 's/-N/-X/'| while read -r l; do sudo iptables -t nat $l; done
175 }
176
177 # usage mkschroot [-] distro codename packages
178 # - means no piping in of sources.list
179 mkschroot() {
180 local force=false
181 while [[ $1 == -* ]]; do
182 case $1 in
183 -f) force=true; shift ;;
184 -s)
185 sources="$2"
186 if [[ ! -s $sources ]]; then
187 echo mkschroot: error: sources file $sources does not exist or is empty
188 return 1
189 fi
190 shift 2
191 ;;
192 esac
193 done
194 distro=$1
195 shift
196 case $distro in
197 trisquel)
198 repo=http://mirror.fsf.org/trisquel/
199 ;;
200 ubuntu)
201 repo=http://archive.ubuntu.com/ubuntu/
202 ;;
203 debian)
204 repo=http://deb.debian.org/debian/
205 ;;
206 esac
207 n=$1
208
209 shift
210 if ! $force && schroot -l | grep -xFq chroot:$n; then
211 echo "$0: $n schroot already installed, skipping"
212 return 0
213 fi
214 apps=($@)
215 d=/nocow/schroot/$n
216 sd /etc/schroot/chroot.d/$n.conf <<EOF
217 [$n]
218 description=$n
219 type=directory
220 directory=$d
221 profile=desktop
222 preserve-environment=true
223 users=$USER,user2
224 EOF
225 cd
226 if [[ ! -e $d/bin ]]; then
227 sudo mkdir -p $d
228 # resolvconf otherwise schroot fails with
229 # cp: not writing through dangling symlink '/var/run/schroot/mount/flidas-7a2362e0-81b3-4848-92c1-610203ef5976/etc/resolv.conf'
230 sudo debootstrap --exclude=resolvconf $n $d $repo
231 fi
232 if [[ $sources ]]; then
233 sudo install -m 644 $sources $d/etc/apt/sources.list
234 fi
235 sudo chroot $d apt-get update
236 sudo DEBIAN_FRONTEND=noninteractive chroot $d apt-get -y dist-upgrade --purge --auto-remove
237 sudo cp -P {,$d}/etc/localtime
238 if (( ${#apps[@]} )); then
239 sudo DEBIAN_FRONTEND=noninteractive schroot -c $n -- apt-get install --allow-unauthenticated -y ${apps[@]}
240 fi
241 }
242
243
244 # note: this is incomplete and untested.
245 # https://wiki.archlinux.org/index.php/Install_Arch_Linux_from_existing_Linux#Creating_a_chroot
246 mkarchchroot() {
247 local tarball mirror
248 mirror=https://mirrors.edge.kernel.org/archlinux/iso/latest/
249 tarball=$(curl -s $mirror | sed -nr 's/.*"(archlinux-bootstrap-.*-x86_64.tar.gz)".*/\1/p')
250 wget -O /tmp/arch.tar.gz https://mirrors.edge.kernel.org/archlinux/iso/latest/$tarball
251 s mkdir -p /nocow/schroot/arch
252 cd _/nocow/schroot/arch
253 s sed -i '/## United States/,/^$/s,^#,,' etc/pacman.d/mirrorlist
254 # error: could not determine cachedir mount point /var/cache/pacman/pkg
255 s sed -i /^CheckSpace/d etc/pacman.conf
256 chroot . /bin/bash -s <<'EOF'
257 pacman-key --init
258 pacman-key --populate archlinux
259 pacman -Syyu
260 EOF
261 # example of building an aur package:
262 # pacman -Sy base-devel wget
263 # useradd -m iank
264 # f=$target/etc/sudoers
265 # line='iank ALL=(ALL) NOPASSWD: ALL'
266 # if [[ ! -e $f ]] || ! grep -xF "$line" $f; then
267 # echo "$line" >> $f
268 # fi
269 # su iank
270 # wget https://aur.archlinux.org/cgit/aur.git/snapshot/anbox-image-gapps.tar.gz
271 # tar xzf anbox-image-gapps.tar.gz
272 # cd anbox-image-gapps
273 # makepkg -s
274 }
275
276
277 # clock back in to timetrack from last entry
278 tback() {
279 sqlite3 /p/.timetrap.db "update entries set end = NULL where id = (select max(id) from entries);"
280 }
281
282 # sshfs example:
283 # s sshfs bu@$host:/bu/home/md /bu/mnt -o reconnect,ServerAliveInterval=20,ServerAliveCountMax=30 -o allow_other
284
285 eqgo() {
286 enn -M $(exiqgrep -i -r.\*)
287 }
288 eqgo1() {
289 enn -M $(exipick -i -r.\*|h1)
290 }
291
292
293 gnupload(){
294 /a/f/gnulib/build-aux/gnupload "$@"
295 }
296
297 abrowserrmcompat() {
298 local f
299 ngset
300 f=(/p/c/firefox*/compatibility.ini)
301 if (( ${#f[@]} )); then
302 rm ${f[@]}
303 fi
304 ngreset
305 }
306
307 checkre() {
308 s checkrestart -b /a/bin/ds/checkrestart-blacklist -pv
309 }
310
311 cp-blocked-domains-to-brains() {
312 cp /a/f/ans/roles/exim/files/mx/simple/etc/exim4/bad-sender_domains /a/f/brains/sysadmin/kb/blocked_email_domains.mdwn
313 }
314 cp-blocked-domains-to-ansible() {
315 cp /a/f/brains/sysadmin/kb/blocked_email_domains.mdwn /a/f/ans/roles/exim/files/mx/simple/etc/exim4/bad-sender_domains
316 }
317
318
319 anki() {
320 # crashes on adding new cards in t9
321 schroot -c buster -- anki
322 }
323
324 acat() {
325 ngset
326 hrcat /m/md/alerts/{cur,new}/*
327 ngreset
328 hr; echo bk; hr
329 ssh bk.b8.nz "shopt -s nullglob; hrcat /m/md/INBOX/new/* /m/md/INBOX/cur/*"
330 }
331 aclear() {
332 ngset
333 rm -f /m/md/alerts/{cur,new}/*
334 ngreset
335 ssh bk.b8.nz "shopt -s nullglob; rm -f /m/md/INBOX/new/* /m/md/INBOX/cur/*"
336 system-status _
337 }
338
339 alerts() {
340 find /var/local/cron-errors /home/iank/cron-errors /sysd-mail-once-state -type f
341 }
342 ralerts() { # remote alerts
343 local ret shell
344 # this list is duplicated in check-remote-mailqs
345 for h in bk je li frodo kwwg x3wg x2wg kdwg sywg; do
346 echo $h:
347 shell="ssh $h"
348 if [[ $HOSTNAME == "${h%wg}" ]]; then
349 shell=
350 fi
351 ret=0
352 $shell find /var/local/cron-errors /home/iank/cron-errors /sysd-mail-once-state -type f || ret=$?
353 if (( ret )); then
354 echo ret:$ret
355 fi
356 done
357 }
358
359 ap() {
360 # pushd in case current directory has an ansible.cfg file
361 pushd /a/xans >/dev/null
362 ansible-playbook -v -l ${1:- $(hostname -f)} site.yml
363 popd >/dev/null
364 }
365 aw() {
366 pushd /a/work/ans >/dev/null
367 time ansible-playbook -i inventory adhoc.yml "$@"
368 popd >/dev/null
369 }
370 ad() {
371 pushd /a/bin/distro-setup/a >/dev/null
372 ansible-playbook site.yml "$@"
373 popd >/dev/null
374 }
375
376 astudio() {
377 # googling android emulator libGL error: failed to load driver: r600
378 # lead to http://stackoverflow.com/a/36625175/14456
379 export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1
380 /a/opt/android-studio/bin/studio.sh "$@" &r;
381 }
382
383
384 iki() {
385 local url path
386 if [[ $1 ]]; then
387 path="$*"
388 else
389 read -r -p "enter path" path
390 fi
391 url=$(readlink -f "$path")
392 url="https://brains.fsf.org/wiki/${url#*brains/}"
393 url="${url%.mdwn}"
394 echo "$url"
395 # /f/brains/sysadmin/interns/2022/nick_shrader/intro_blog_post.mdwn
396 # becomes
397 # https://brains.fsf.org/wiki/sysadmin/interns/2022/nick_shrader/intro_blog_post
398
399 }
400
401 # Generate beet smartplaylists for navidrome.
402 # for going in the reverse direction, run
403 # /b/ds/navidrome-playlist-export
404 beetsmartplaylists() {
405 install -m 0700 -d /tmp/ianbeetstmp
406 beet splupdate
407 # kill off any playlists we deleted. they will still need manual
408 # killing from a navidrome client.
409 rm -rf /i/converted/beetsmartplaylists
410 mkdir -p /i/converted/beetsmartplaylists
411 for f in /tmp/ianbeetstmp/*; do
412 sed 's,^/i/m,/i/converted,;s,\.flac$,.mp3,' "$f" >"/i/converted/beetsmartplaylists/${f##*/}"
413 rm "$f"
414 done
415 rmdir /tmp/ianbeetstmp
416 }
417
418 # Export beets ratings into navidrome
419 beetrating() {
420 local tmp tmpfile myuser userid rating path cpath sqlpath
421 # plucked this from the db. im the only user.
422 userid=23cc2eb9-e35e-4811-a0f0-d5f0dd6eb634
423 tmpfile=$(mktemp)
424 beet ls -f '$rating $path' ^genre:spoken-w ^genre:skit rating:2..5 >$tmpfile
425 while read -r rating path; do
426 tmp="/i/converted${path#/i/m}"
427 cpath="${tmp%.*}.mp3" # converted path
428 sqlpath="${cpath//\'/\'\'}"
429 old_rating=$(sqlite3 /i/navidrome/navidrome.db "select rating from annotation inner join media_file on item_id = id where path = '$sqlpath' and item_type = 'media_file';")
430 if [[ $old_rating ]]; then
431 if [[ $old_rating != $rating ]]; then
432 # https://stackoverflow.com/a/50317320
433 m sqlite3 /i/navidrome/navidrome.db "
434 update annotation set rating = $rating
435 where item_id in (
436 select media_file.id from annotation inner join media_file on annotation.item_id = media_file.id
437 where media_file.path = '$sqlpath' and annotation.item_type = 'media_file' );"
438 fi
439 else
440 # /a/opt/navidrome/persistence/sql_annotations.go v0.48.0
441 # https://www.sqlite.org/lang_insert.html
442 m sqlite3 /i/navidrome/navidrome.db "insert into annotation select '$(uuidgen)', '$userid', id, 'media_file', 0, NULL, $rating, 0, NULL from media_file where path = '$sqlpath';"
443 fi
444 #sqlite3 /i/navidrome/navidrome.db "select path from annotation inner join media_file on item_id = id where rating = $r;"
445 done <$tmpfile
446 }
447
448 # Do transcoding and hardlinking of audio files for navidrome.
449 #
450 # This deletes files in the converted directory which should no longer
451 # be there due to a rename of the unconverted file.
452 beetconvert() {
453 local l query
454 local -A paths
455 query="^genre:spoken-w ^genre:skit ^lesser_version:t ^rating:1"
456 # redirect is to avoid printing every file
457 beet convert -y $query >/dev/null 2> >(grep -v '^convert: Skipping' ||:)
458
459 ## begin removal of files that are leftover from previous conversion,
460 # eg, previously rated > 1, now rated 1.
461 while read -r l; do
462 convertedpath="/i/converted${l#/i/m}"
463 case $convertedpath in
464 *.flac) convertedpath="${convertedpath%.flac}.mp3" ;;
465 esac
466 paths[$convertedpath]=t
467 done < <(beet ls -f '$path' $query)
468 while read -r l; do
469 if [[ ! ${paths[$l]} ]]; then
470 rm -v "$l"
471 fi
472 # note: the pruning is duplicative of filtering on name, but whatever.
473 done < <(find /i/converted -path /i/converted/beetsmartplaylists -prune -o \( -type f -print \) -name '*.mp3' -o -name '*.m4a')
474 ## end
475 }
476
477 # tag with beets.
478 # usage: beetag QUERY
479 # it lists the query, reads an input char for tagging one by one.
480 # 1-5 = set rating
481 # a-x 0 6-9 / . , = set genre/playlist. (available buttons: ` \ ) ] [
482 # q = quit
483 # y = toggle to setting rare genres
484 # z = put the player in the foreground
485 # enter = next song
486 # ' = toggle playing of songs, also replays current song if hit twice
487 # ; = go to previous song
488 # _ = delete file, remove from library
489 # -/+ = decrease / increase volume
490 #
491 # note, you may want to change the play command for doing rapid taging
492 # by immediately jumping forward into the song. this is set in the beets
493 # config yaml.
494 beetag() {
495 local last_genre_i fstring tag id char new_item char_i genre tag remove doplay i j random
496 local do_rare_genres read_wait
497 local -a genres pl_tags buttons button_map ids tags rare_genres tmp_tags
498 local -A button_i
499 local -i volume
500 do_rare_genres=false
501 random=false
502 volume=70
503 read_wait=2
504 case $1 in
505 -r)
506 random=true
507 shift
508 ;;
509 esac
510 if (( ! $# )); then
511 echo beetag: error expected a query arg >&2
512 return 1
513 fi
514 doplay=true
515 genres=(
516 # gangsta rap / angry rap. something like g-rap would make beet queries for genre:rap include it
517 arp
518 ambient
519 avant
520 blues
521 classical
522 # slow instrumental. todo: reclassify some ambient into this.
523 chill
524 country
525 # like power glove
526 dark-wave
527 # lyrical edm. todo: some pop needs reclassification to this
528 dance
529 hardcore
530 instrumental
531 latin
532 metal
533 # mq = mac quale. similar to the mr robot soundtracks.
534 # slow, foreboding. usually electronic.
535 mq
536 pop
537 rap
538 rock
539 # like rain by brian crain. mostly slow broody piano
540 sleep
541 techno
542 world
543 )
544 # because we were destined to run out of single key buttons.
545 rare_genres=(
546 jazz
547 musical
548 noise
549 skit
550 spoken-w
551 )
552 pl_tags=(
553 ## cross-genre tags that dont really make a playlist
554 expl
555 # songs i like but they get old fast due to feeling gimicky, or cringy after a while.
556 gimicky
557 # alternate version of a song we already have which isn't as good
558 lesser_version
559 # anything sad which i sometimes like or avoid.
560 sad
561
562 ## playlists
563 # intimate, love
564 love
565 # favorite songs pump up songs
566 pump1
567 # favorite rap pump up songs, allows more songs than pump1
568 pumprap
569 # heart rending, spine tickling
570 rend
571 # for running
572 run
573 )
574 last_genre_i=$(( ${#genres[@]} - 1 ))
575 buttons=( {a..p} {r..w} 0 {6..9} , . / )
576 button_map=(${genres[@]} ${pl_tags[@]})
577 fstring=
578 for tag in "${pl_tags[@]}"; do
579 fstring+="%ifdef{$tag,$tag }"
580 done
581
582 for (( i=0; i<${#buttons[@]}; i++ )); do
583 button_i[${buttons[i]}]=$i
584 done
585 beet ls -f '%ifdef{rating,$rating }'"$fstring"', $genre $artist - $album - $title' "$@"
586 mapfile -t ids < <(beet ls -f '$id' "$@" | { if $random; then sort -R; else cat; fi; } )
587 for (( j=0; j<${#ids[@]}; j++ )); do
588 hr
589 id=${ids[j]}
590 lsout="$(beet ls -f '%ifdef{rating,$rating }'"$fstring"', $genre $id $artist - $album - $title' "id:$id")"
591 tags=( ${lsout%%,*} )
592 printf "%s\n" "$lsout"
593 for (( i=0; i<${#button_map[@]}; i++ )); do
594 echo ${buttons[i]} ${button_map[i]}
595 done
596 if $doplay; then
597 beet play --args=--volume=$volume "id:$id" &
598 fi
599 while true; do
600 char=
601 if $doplay; then
602 ret=0
603 read -r -N 1 -s -t $read_wait char || ret=$?
604 read_wait=2
605 # Automatically skip to the next song if this one ends, unless
606 # we turn off the autoplay.
607 if (( ret == 142 )) || [[ ! $char ]]; then
608 if bg %% &>/dev/null; then
609 continue
610 else
611 break
612 fi
613 fi
614 else
615 read -r -N 1 -s char
616 fi
617 if [[ $char == $'\n' ]]; then
618 kill %% ||: &>/dev/null
619 break
620 fi
621 case $char in
622 ";")
623 kill %% ||: &>/dev/null
624 j=$(( j - 2 ))
625 break
626 ;;
627 "'")
628 if $doplay; then
629 doplay=false
630 else
631 doplay=true
632 kill %% ||: &>/dev/null
633 beet play --args=--volume=$volume "id:$id" &
634 fi
635 continue
636 ;;
637 _)
638 kill %% ||: &>/dev/null
639 m beet rm --delete --force "id:$id"
640 break
641 ;;
642 [1-5])
643 beet modify -y "id:$id" rating=$char
644 continue
645 ;;
646 -)
647 volume=$(( volume - 5 ))
648 if (( volume < 0 )); then
649 volume=0
650 fi
651 echo volume=$volume
652 continue
653 ;;
654 q)
655 kill %% ||: &>/dev/null
656 return
657 ;;
658 +)
659 volume+=5
660 if (( volume > 130 )); then
661 volume=130
662 fi
663 echo volume=$volume
664 continue
665 ;;
666 y)
667 if $do_rare_genres; then
668 do_rare_genres=false
669 button_map=(${genres[@]} ${pl_tags[@]})
670 last_genre_i=$(( ${#rare_genres[@]} - 1 ))
671 else
672 do_rare_genres=true
673 button_map=(${rare_genres[@]} ${pl_tags[@]})
674 last_genre_i=$(( ${#genres[@]} - 1 ))
675 fi
676 local -A button_i
677 for (( i=0; i<${#buttons[@]}; i++ )); do
678 button_i[${buttons[i]}]=$i
679 done
680 for (( i=0; i<${#button_map[@]}; i++ )); do
681 echo ${buttons[i]} ${button_map[i]}
682 done
683 continue
684 ;;
685 z)
686 # if we ctrl-z, it will put the whole function into sleep. so
687 # basically, we can't return from a foregrounded mpv like we
688 # would like to without some strange mechanism I can't think
689 # of. So, instead, detect ctrl-c and wait a while for prompt
690 # input. One idea would be to use a music player like mpd where
691 # we can send it messages.
692 if ! fg; then
693 sleep_wait=10
694 fi
695 continue
696 ;;
697 esac
698 char_i=${button_i[$char]}
699 new_item=${button_map[$char_i]}
700 if [[ ! $char_i || ! $new_item ]]; then
701 echo "error: no mapping of input: $char found, try again"
702 continue
703 fi
704 if (( char_i <= last_genre_i )); then
705 m beet modify -y "id:$id" genre=$new_item
706 else
707 remove=false
708 tmp_tags=()
709 for tag in ${tags[@]}; do
710 if [[ $new_item == "$tag" ]]; then
711 remove=true
712 else
713 tmp_tags+=("$tag")
714 fi
715 done
716 if $remove; then
717 tags=("${tags[@]}")
718 m beet modify -y "id:$id" "$new_item!"
719 else
720 tags+=("$new_item")
721 m beet modify -y "id:$id" $new_item=t
722 fi
723 fi
724 done
725 done
726 }
727
728 # usage: FILE|ALBUM_DIR [GENRE]
729 beetadd() {
730 local import_path genre_arg single_track_arg
731 import_path="$1"
732 if [[ ! -e $import_path ]]; then
733 echo "beetadd error: path does not exist"
734 fi
735 if [[ $2 ]]; then
736 genre_arg="--set genre=$2"
737 fi
738 if [[ -f $import_path ]]; then
739 single_track_arg=-s
740 fi
741 beet import --set totag=t $single_track_arg $genre_arg "$import_path"
742 beetag totag:t
743 beet modify -y totag:t "totag!"
744 }
745
746 # update navidrome music data after doing beets tagging
747 beet2nav() {
748 beetconvert
749 beetsmartplaylists
750 beetrating
751 }
752
753 # pull in beets library locally
754 beetpull() {
755 if [[ ! -e /i ]]; then
756 s mkdir /i
757 s chown iank:iank /i
758 sshfs b8.nz:/i /i
759 fi
760 }
761
762 # escape regex.
763 #
764 # This is not perfect but generally good enough. It escapes all
765 # metachars listed man 3 pcrepattern.
766 er() {
767 sed 's/[]\\^$.[|()?*+{}]/[&]/g; s/\^/\\^/g' <<<"$*"
768 }
769
770 # usage beegenre QUERY
771 #
772 # beet set genre for QUERY based on existing artist most used genre on
773 #
774 # inverse of query for each artist found in QUERY. If query starts with
775 # "artist:" it is used as the artist instead of each artist in QUERY.
776 #
777 beegenre() {
778 local artist artregex genre term singleartist
779 local -a artists genres terms
780 singleartist=false
781 case $1 in
782 artist:*)
783 singleartist=true
784 artist="$term"
785 ;;
786 esac
787 if $singleartist; then
788 read count genre < <(beet ls -f '$genre' "$artist" "${@/#/^}" | sort | uniq -c | sort -n | tail -n1) ||:
789 beet modify "$artist" "$@" genre=$genre
790 else
791 while read -r artist; do
792 artregex=$(er "$artist")
793 read count genre < <(beet ls -f '$genre' "artist::^$artregex$" "${@/#/^}" | sort | uniq -c | sort -n | tail -n1) || continue
794 if [[ $count ]]; then
795 artists+=("$artregex")
796 genres+=("$genre")
797 echo "beet modify -y $@ \"artist::^$artist$\" genre=$genre # $count"
798 fi
799 done < <(beet ls -f '$artist' "$@" | sort -u)
800 read -r -N 1 -s -p "Y/n " char
801 case $char in
802 [Yy$'\n'])
803 for (( i=0; i<${#artists[@]}; i++ )); do
804 beet modify -y "$@" "artist::^${artists[i]}$" genre=${genre[i]}
805 done
806 ;;
807 esac
808 fi
809 }
810
811 # note, to check for glue records
812 # First, find some the .org nameservers:
813 # dig +trace iankelling.org
814 # then, query one:
815 # dig ns1.iankelling.org @b0.org.afilias-nst.org.
816
817 # Now, compare for a domain that does have glue records setup (note the A
818 # and AAAA records in ADDITIONAL SECTION, those are glue records like the
819 # one I'm asking for):
820
821 # $ dig ns1.gnu.org @b0.org.afilias-nst.org.
822
823 # todo: make sm pull/push use systemd instead of the journal cat command
824 bbk() { # btrbk wrapper
825 local ret=0
826 c /
827 local active=true
828 systemctl is-active btrbk.timer || active=false
829 if $active; then
830 ser stop btrbk.timer
831 fi
832 btrbk_is_active=$(systemctl is-active btrbk.service ||:)
833 case $btrbk_is_active in
834 inactive|failed) : ;;
835 *)
836 echo "bbk: error: systemctl is-active btrbk.service output: $btrbk_is_active"
837 if $active; then ser start btrbk.timer; fi
838 return 1
839 ;;
840 esac
841 # run latest
842 install-my-scripts
843 # todo: consider changing this to srun and having the args come
844 # from a file like /etc/default/btrbk, like is done in exim
845 s jdo btrbk-run "$@"
846 if $active; then
847 if (( ret )); then
848 echo bbk: WARNING: btrbk.timer not restarted due to failure
849 else
850 ser start btrbk.timer
851 fi
852 fi
853 return $ret
854 }
855
856 faimon() {
857 fai-monitor | pee cat "fai-monitor-gui -"
858 }
859
860 bfg() { java -jar /a/opt/bfg-1.12.14.jar "$@"; }
861
862 bigclock() {
863 xclock -digital -update 1 -face 'arial black-80:bold'
864 }
865
866 nnn() { /a/opt/nnn -H "$@"; }
867
868 locat() { # log-once cat
869 local files
870 ngset
871 files=(/var/local/cron-errors/* /home/iank/cron-errors/* /sysd-mail-once-state/*)
872 case ${#files[@]} in
873 0) : ;;
874 1)
875 echo ${files[0]}
876 head ${files[0]}
877 ;;
878 *)
879 head ${files[@]}
880 ;;
881 esac
882 ngreset
883 }
884
885 scr() {
886 screen -RD "$@"
887 }
888
889 # usage: first get an adb shell on the phone.
890 #
891 # just followed instructions in readme at
892 # https://github.com/Yuubi-san/ceb-tools
893 # tried to use ceb2txt but it failed because of schema
894 # slightly different than what it expected.
895 cheogram-get-logs() {
896 adb shell rm -r /storage/emulated/0/Download/Cheogram/Backup
897 read -p "do cheogram backup on phone, do not enable extra cheogram data. press any key when done"
898 cd /p/cheogram
899 rm -rf Backup b
900 adb pull /storage/emulated/0/Download/Cheogram/Backup
901 sqlite3 b </a/opt/ceb-tools/schema.sql
902 echo "note: the next step took 39 seconds last time i measured"
903 /a/opt/ceb-tools/ceb2sqlgz Backup/iank@fsf.org.ceb <pas | gunzip | sqlite3 b
904 rm -r Backup
905 }
906
907 # usage: cheologs [DAYS_LIMIT]
908 # default days is 100
909 cheologs() {
910 local days q
911 days=${1:-100}
912 q="
913 select
914 datetime(substr(timeSent,0,11), 'unixepoch'),
915 replace(replace(counterpart,'@fsf.org',''),
916 '@conference.fsf.org',''),
917 body
918 from messages
919 where timeSent > $(( (EPOCHSECONDS - days * 60 * 60 * 24) * 1000 ))
920 order by timeSent;"
921 sqlite3 /p/cheogram/b ".mode tabs" "$q" | less
922 }
923
924 mycheologs() {
925 local days q
926 days=${1:-16}
927 q="
928 select
929 datetime(substr(timeSent,0,11), 'unixepoch'),
930 body
931 from messages
932 where timeSent > $(( (EPOCHSECONDS - days * 60 * 60 * 24) * 1000 ))
933 and counterpart = 'office@conference.fsf.org/iank'
934 order by timeSent;"
935 sqlite3 /p/cheogram/b ".mode tabs" "$q" | sed 's/ /./' | less
936 }
937
938 # version of jdo for my non-root user
939 jdo() {
940 # comparison of alternative logging methods:
941 #
942 # systemd-run command (what this function does)
943 #
944 # If there is a user prompt, the program will detect that it is not
945 # connected to a terminal and act in a non-interactive way, skipping
946 # the prompt. This has the benefit that you know exactly how the
947 # program will act if you want to move it into a service that runs
948 # automatically.
949 #
950 # If run with sudo and command is a shell script which does a sleep,
951 # it can (sometimes?) output some extra whitespace in front of
952 # messages, more for each subsequent message. This can be avoided by
953 # becoming root first.
954 #
955 # It logs the command's pid and exit code, which is nice.
956 #
957 #
958 ### command |& ts | tee file.log
959 #
960 # If there is a user prompt, like "read -p prompt var", it will hang
961 # without outputting the prompt.
962 #
963 # I've had a few times where ts had an error and I wasn't totally sure
964 # if it was really the command or ts having the problem.
965 #
966 # Sometimes some output will get hidden until you hit enter.
967 #
968 #
969 ### command |& pee cat logger
970 #
971 # This seems to work. I need to test more.
972 #
973 #
974 ### command |& logger -s
975 #
976 # User prompts get confusingly prefixed to earlier output, and all log
977 # entries get prefixed with annoying priority level.
978 #
979 #
980 ### systemd-cat
981 #
982 # Had a few problems. One major one is that it exited in the middle of
983 # a command on systemctl daemon-reload
984 #
985 # Related commands which can log a whole session: script, sudo, screen
986 local cmd cmd_name jr_pid ret
987 ret=0
988 cmd="$1"
989 shift
990 cmd_name=${cmd##*/}
991 if [[ $cmd != /* ]]; then
992 cmd=$(type -P "$cmd")
993 fi
994 # -q = quiet
995 journalctl -qn2 -f -u "$cmd_name" &
996 # Trial and error of time needed to avoid missing initial lines.
997 # .5 was not reliable. 1 was not reliable. 2 was not reliable
998 sleep 4
999 jr_pid=$!
1000 # note, we could have a version that does system --user, but if for example
1001 # it does sudo ssh, that will leave a process around that we can't kill
1002 # and it will leave the unit hanging around in a failed state needing manual
1003 # killing of the process.
1004 s systemd-run --uid $(id -u) --gid $(id -g) \
1005 -E SSH_AUTH_SOCK=/run/openssh_agent \
1006 --unit "$cmd_name" --wait --collect "$cmd" "$@" || ret=$?
1007 # The sleep lets the journal output its last line
1008 # before the prompt comes up.
1009 sleep .5
1010 kill $jr_pid &>/dev/null ||:
1011 unset jr_pid
1012 fg &>/dev/null ||:
1013 # this avoids any err-catch
1014 (( $ret == 0 )) || return $ret
1015 }
1016
1017 # service run, and watch the output
1018 srun() {
1019 local unit
1020 ret=0
1021 unit=$1
1022 journalctl -qn2 -f -u $unit &
1023 systemctl start $unit
1024 sleep 2
1025 kill $jr_pid &>/dev/null ||:
1026 unset jr_pid
1027 fg &>/dev/null ||:
1028 }
1029
1030 sm() { # switch mail host
1031 local tmp keyhash
1032 c /
1033 # run latest
1034 keyhash=$(s ssh-keygen -lf /root/.ssh/home | awk '{print $2}')
1035 tmp=$(s ssh-add -l | awk '$2 == "'$keyhash'"')
1036 if [[ ! $tmp ]]; then
1037 s ssh-add /root/.ssh/home
1038 fi
1039 install-my-scripts
1040 s jdo switch-mail-host "$@"
1041 return $ret
1042 }
1043 sh2() { # switch host2
1044 local tmp keyhash
1045 c /
1046 # run latest
1047 keyhash=$(s ssh-keygen -lf /root/.ssh/home | awk '{print $2}')
1048 tmp=$(s ssh-add -l | awk '$2 == "'$keyhash'"')
1049 if [[ ! $tmp ]]; then
1050 s ssh-add /root/.ssh/home
1051 fi
1052 install-my-scripts
1053 s jdo switch-host2 "$@"
1054 return $ret
1055 }
1056
1057 # shellcheck disable=SC2120
1058 lipush() {
1059 # note, i had --delete-excluded, but that deletes all files in --exclude-from on
1060 # the remote site, which doesn't make sense, so not sure why i had it.
1061 local p a
1062 # excluding emacs for now
1063 #p=(/a/opt/{emacs-debian11{,-nox},mu,emacs} /a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts})
1064 p=(/a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts})
1065 a="-ahviSAXPH --specials --devices --delete --relative --exclude-from=/p/c/li-rsync-excludes"
1066 ret=0
1067 for h in li je bk; do
1068 m s rsync "$@" $a ${p[@]} /p/c/machine_specific/$h root@$h.b8.nz:/
1069 ## only li is debian11
1070 #p[0]=/a/opt/emacs-trisuqel10
1071 #p[1]=/a/opt/emacs-trisquel10-nox
1072 done
1073 m s rsync "$@" -ahviSAXPH root@li.b8.nz:/a/h/proposed-comments/ /a/h/proposed-comments || ret=$?
1074 return $ret
1075 }
1076 bkpush() { # no emacs. for running faster.
1077 p=(/a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts})
1078 a="-ahviSAXPH --specials --devices --delete --relative --exclude-from=/p/c/li-rsync-excludes"
1079 ret=0
1080 m rsync "$@" $a ${p[@]} /p/c/machine_specific/bk root@bk.b8.nz:/ || ret=$?
1081 return $ret
1082 }
1083 jepush() { # no emacs. for running faster.
1084 p=(/a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts})
1085 a="-ahviSAXPH --specials --devices --delete --relative --exclude-from=/p/c/li-rsync-excludes"
1086 ret=0
1087 m rsync "$@" $a ${p[@]} /p/c/machine_specific/je root@je.b8.nz:/ || ret=$?
1088 return $ret
1089 }
1090
1091 bindpush() {
1092 dsign iankelling.org expertpathologyreview.com zroe.org amnimal.ninja
1093 lipush
1094 for h in li bk; do
1095 m sl $h <<'EOF'
1096 source ~/.bashrc
1097 m dnsup
1098 EOF
1099 done
1100 }
1101 bindpushb8() {
1102 lipush
1103 for h in li bk; do
1104 m sl $h <<'EOF'
1105 source ~/.bashrc
1106 m dnsb8
1107 EOF
1108 done
1109 }
1110
1111 dnsup() {
1112 conflink -f
1113 m ser reload named
1114 }
1115 dnsb8() {
1116 local f=/var/lib/bind/db.b8.nz
1117 m ser stop named
1118 m sleep 1
1119 m sudo rm -fv $f.jnl $f.signed.jnl
1120 m sudo install -m 644 -o bind -g bind /p/c/machine_specific/vps/bind-initial/db.b8.nz $f
1121 m ser restart named
1122 }
1123 dnsecgen() {
1124 # keys generated like this
1125 # because of https://ftp.isc.org/isc/dnssec-guide/dnssec-guide.pdf
1126 # https://blog.apnic.net/2019/05/23/how-to-deploying-dnssec-with-bind-and-ubuntu-server/
1127
1128 # key length is longer than that guide because
1129 # we are using those at fsf and when old key lengths
1130 # become insecure, I want some extra time to update.
1131 # dnsecgen (in brc2)
1132
1133 local zone=$1
1134 dnssec-keygen -a RSASHA256 -b 2048 $zone
1135 dnssec-keygen -f KSK -a RSASHA256 -b 4096 $zone
1136 for f in K$zone.*.key; do
1137 # eg Kb8.nz.+008+47995.key tag=47995
1138 # in dnsimple, you add the long string from this.
1139 # in gandi, you add the long string from the .key file,
1140 # then see that the digest matches the ds.
1141 echo "tag is the number after DS"
1142 dnssec-dsfromkey -a SHA-256 $f
1143 done
1144 # For b8.nz, we let bind read the keys and sign, and
1145 # right now they have root ownership, so let them
1146 # get group read.
1147 chmod g+r *.private
1148 }
1149 dsign() {
1150 # create .signed file
1151 # note: full paths probably not needed.
1152 local arg
1153 for arg; do
1154 local zone=${arg#db.}
1155 local dir=/p/c/machine_specific/vps/filesystem/var/lib/bind
1156 dnssec-signzone -S -e +31536000 -o $zone -K $dir -d $dir $dir/db.$zone
1157 done
1158 }
1159
1160
1161 #### begin bitcoin related things
1162 btc() {
1163 local f=/etc/bitcoin/bitcoin.conf
1164 # importprivkey will timeout if using the default of 15 mins.
1165 # upped it to 1 hour.
1166 bitcoin-cli -rpcclienttimeout=60000 -$(s grep rpcuser= $f) -$(s grep rpcpassword= $f) "$@"
1167 }
1168 btcusd() { # $1 btc in usd
1169 local price
1170 price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
1171 printf "$%s\n" "$price"
1172 if [[ $1 ]]; then
1173 printf "$%.2f\n" "$(echo "scale=4; $price * $1"| bc -l)"
1174 fi
1175 }
1176 usdbtc() { # $1 usd in btc
1177 local price
1178 price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
1179 printf "$%s\n" "$price"
1180 if [[ $1 ]]; then
1181 # 100 mil satoshi / btc. 8 digits after the 1.
1182 printf "%.8f btc\n" "$(echo "scale=10; $1 / $price "| bc -l)"
1183 fi
1184 }
1185 satoshi() { # $1 satoshi in usd
1186 local price
1187 price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
1188 price=$(echo "scale=10; $price * 0.00000001"| bc -l)
1189 printf "$%f\n" "$price"
1190 if [[ $1 ]]; then
1191 printf "$%.2f\n" "$(echo "scale=10; $price * $1"| bc -l)"
1192 fi
1193 }
1194 #### end bitcoin related things
1195
1196
1197
1198 cbfstool () { /a/opt/coreboot/build/cbfstool "$@"; }
1199
1200
1201 cgpl()
1202 {
1203 if (($#)); then
1204 cp /a/bin/data/COPYING "$@"
1205 else
1206 cp /a/bin/data/COPYING .
1207 fi
1208 }
1209
1210 capache()
1211 {
1212 if (($#)); then
1213 cp /a/bin/data/LICENSE "$@"
1214 else
1215 cp /a/bin/data/LICENSE .
1216 fi
1217 }
1218
1219 chrome() {
1220 if type -p chromium &>/dev/null; then
1221 cmd=chromium
1222 else
1223 cd /
1224 cmd="schroot -c bullseye chromium"
1225 CHROMIUM_FLAGS='--enable-remote-extensions' $cmd &r
1226 fi
1227 }
1228
1229
1230 # do all tee.
1231 # pipe to this, or just type like a shell
1232 # todo: test this
1233 dat() {
1234 tee >(ssh frodo.b8.nz) >(ssh x2) >(ssh tp.b8.nz) >(ssh kw) >(ssh tp.b8.nz)
1235 }
1236 da() { # do all
1237 local host
1238 for host in x2 kw tp.b8.nz x3.b8.nz frodo.b8.nz; do
1239 ssh $host "$@"
1240 done
1241 }
1242
1243
1244 debian_pick_mirror () {
1245 # netselect-apt finds a fast mirror.
1246 # but we need to replace the mirrors ourselves,
1247 # because it doesnt do that. best it can do is
1248 # output a basic sources file
1249 # here we get the server it found, get the main server we use
1250 # then substitute all instances of one for the other in the sources file
1251 # and backup original to /etc/apt/sources.list-original.
1252 # this is idempotent. the only way to identify debian sources is to
1253 # note the original server, so we put it in a comment so we can
1254 # identify it later.
1255 local file
1256 file=$(mktemp -d)/f # safe way to get file name without creating one
1257 sudo netselect-apt -o "$file" || return 1
1258 url=$(grep ^\\w $file | head -n1 | awk '{print $2}')
1259 sudo cp -f /etc/apt/sources.list /etc/apt/sources.list-original
1260 sudo sed -ri "/http.us.debian.org/ s@( *[^ #]+ +)[^ ]+([^#]+).*@\1$url\2# http.us.debian.org@" /etc/apt/sources.list
1261 sudo apt-get update
1262 }
1263 digme() {
1264 digdiff @ns{1,2}.iankelling.org "$@"
1265 }
1266
1267 tsr() { # ts run
1268 "$@" |& ts || return $?
1269 }
1270
1271 dup() {
1272 local ran_d
1273 ran_d=false
1274 system-status _
1275 case $PS1 in
1276 *[\ \]]D\ *)
1277 pushd /
1278 /b/ds/distro-begin |& ts || return $?
1279 /b/ds/distro-end |& ts || return $?
1280 popd
1281 ran_d=true
1282 ;;&
1283 *[\ \]]DB\ *)
1284 pushd /
1285 /b/ds/distro-begin |& ts || return $?
1286 popd
1287 ran_d=true
1288 ;;
1289 *[\ \]]DE\ *)
1290 pushd /
1291 /b/ds/distro-end |& ts || return $?
1292 popd
1293 ran_d=true
1294 ;;&
1295 *CONFLINK*)
1296 if ! $ran_d; then
1297 conflink
1298 fi
1299 ;;
1300 esac
1301 system-status _
1302 }
1303
1304 envload() { # load environment from a previous: export > file
1305 local file=${1:-$HOME/.${USER}_env}
1306 eval "$(export | sed 's/^declare -x/export -n/')"
1307 while IFS= read -r line; do
1308 # declare -x makes variables local to a function
1309 eval ${line/#declare -x/export}
1310 done < "$file"
1311 }
1312
1313 failfunc() { asdf a b c; }
1314 failfunc2() { failfunc d e f; }
1315
1316 # one that comes with distros is too old for newer devices
1317 fastboot() {
1318 /a/opt/android-platform-tools/fastboot "$@";
1319 }
1320
1321 kdecd() { /usr/lib/x86_64-linux-gnu/libexec/kdeconnectd; }
1322
1323 bat() {
1324 cat /sys/class/power_supply/BAT0/capacity
1325 }
1326
1327 # List of apps to install/update
1328 # Create from existing manually installed apps by doing
1329 # fdroidcl update
1330 # fdroidcl search -i, then manually removing
1331 # automatically installed/preinstalled apps
1332
1333 #
1334 # # my attempt at recovering from boot loop:
1335 # # in that case, boot to recovery (volume up, home button, power, let go of power after samsun logo)
1336 # # then
1337 # mount /dev/block/mmcblk0p12 /data
1338 # cd /data
1339 # find -iname '*appname*'
1340 # rm -rf FOUND_DIRS
1341 # usually good enough to just rm -rf /data/app/APPNAME
1342 #
1343 # currently broken:
1344 # # causes replicant to crash
1345 # org.quantumbadger.redreader
1346 # org.kde.kdeconnect_tp
1347
1348 # not broke, but wont work without gps
1349 #com.zoffcc.applications.zanavi
1350 # not broke, but not using atm
1351 #com.nutomic.syncthingandroid
1352 # # doesn\'t work on replicant
1353 #net.sourceforge.opencamera
1354 #
1355 fdroid_pkgs=(
1356 net.mullvad.mullvadvpn
1357 org.schabi.newpipe
1358 io.github.subhamtyagi.lastlauncher
1359 io.anuke.mindustry
1360 com.biglybt.android.client
1361 de.marmaro.krt.ffupdater
1362 me.ccrama.redditslide
1363 org.fedorahosted.freeotp
1364 at.bitfire.davdroid
1365 com.alaskalinuxuser.justnotes
1366 com.artifex.mupdf.viewer.app
1367 com.danielkim.soundrecorder
1368 com.fsck.k9
1369 com.ichi2.anki
1370 com.jmstudios.redmoon
1371 com.jmstudios.chibe
1372 org.kde.kdeconnect_tp
1373 com.notecryptpro
1374 com.termux
1375 cz.martykan.forecastie
1376 de.danoeh.antennapod
1377 de.blinkt.openvpn
1378 de.marmaro.krt.ffupdater
1379 eu.siacs.conversations
1380 free.rm.skytube.oss
1381 im.vector.alpha # riot
1382 info.papdt.blackblub
1383 me.tripsit.tripmobile
1384 net.gaast.giggity
1385 net.minetest.minetest
1386 net.osmand.plus
1387 org.isoron.uhabits
1388 org.linphone
1389 org.gnu.icecat
1390 org.smssecure.smssecure
1391 org.yaaic
1392 sh.ftp.rocketninelabs.meditationassistant.opensource
1393 )
1394 # https://forum.xda-developers.com/android/software-hacking/wip-selinux-capable-superuser-t3216394
1395 # for maru,
1396 #me.phh.superuser
1397
1398 fdup() {
1399 local -A installed updated
1400 local p
1401 # tried putting this in go buildscript cronjob,
1402 # but it failed with undefined: os.UserCacheDir. I expect its due to
1403 # an environment variable missing, but its easier just to stick it here.
1404 m go get -u mvdan.cc/fdroidcl || return 1
1405 m fdroidcl update
1406 if fdroidcl search -u | grep ^org.fdroid.fdroid; then
1407 fdroidcl install org.fdroid.fdroid
1408 sleep 5
1409 m fdroidcl update
1410 fi
1411 for p in $(fdroidcl search -i| grep -o "^\S\+"); do
1412 installed[$p]=true
1413 done
1414 for p in $(fdroidcl search -u| grep -o "^\S\+"); do
1415 updated[$p]=false
1416 done
1417 for p in ${fdroid_pkgs[@]}; do
1418 if ! ${installed[$p]:-false}; then
1419 m fdroidcl install $p
1420 # sleeps are just me being paranoid since replicant has a history of crashing when certain apps are installed
1421 sleep 5
1422 fi
1423 done
1424 for p in ${!installed[@]}; do
1425 if ! ${updated[$p]:-true}; then
1426 m fdroidcl install $p
1427 sleep 5
1428 fi
1429 done
1430 }
1431
1432 firefox-default-profile() {
1433 key=Default value=1 section=$1
1434 file=/p/c/subdir_files/.mozilla/firefox/profiles.ini
1435 sed -ri "/^ *$key/d" "$file"
1436 sed -ri "/ *\[$section\]/,/^ *\[[^]]+\]/{/^\s*$key[[:space:]=]/d};/ *\[$section\]/a $key=$value" "$file"
1437 }
1438 fdhome() { #firefox default home profile
1439 firefox-default-profile Profile0
1440 }
1441
1442 fdwork() {
1443 firefox-default-profile Profile4
1444 }
1445
1446 ff() {
1447 if type -P firefox &>/dev/null; then
1448 firefox "$@"
1449 else
1450 iceweasel "$@"
1451 fi
1452 }
1453
1454 fn() {
1455 firefox -P alt "$@" >/dev/null 2>&1
1456 }
1457
1458
1459 fsdiff () {
1460 local missing=false
1461 local dname="${PWD##*/}"
1462 local m="/a/tmp/$dname-missing"
1463 local d="/a/tmp/$dname-diff"
1464 [[ -e $d ]] && rm "$d"
1465 [[ -e $m ]] && rm "$m"
1466 local msize=0
1467 local fsfile
1468 while read -r line; do
1469 fsfile="$1${line#.}"
1470 if [[ -e "$fsfile" ]]; then
1471 md5diff "$line" "$fsfile" && tee -a "/a/tmp/$dname-diff" <<< "$fsfile $line"
1472 else
1473 missing=true
1474 echo "$line" >> "$m"
1475 msize=$((msize + 1))
1476 fi
1477 done < <(find . -type f )
1478 if $missing; then
1479 echo "$m"
1480 (( msize <= 100 )) && cat $m
1481 fi
1482 }
1483 fsdiff-test() {
1484 # expected output, with different tmp dirs
1485 # /tmp/tmp.HDPbwMqdC9/c/d ./c/d
1486 # /a/tmp/tmp.qLDkYxBYPM-missing
1487 # ./b
1488 cd $(mktemp -d)
1489 echo ok > a
1490 echo nok > b
1491 mkdir c
1492 echo ok > c/d
1493 local x
1494 x=$(mktemp -d)
1495 mkdir $x/c
1496 echo different > $x/c/d
1497 echo ok > $x/a
1498 fsdiff $x
1499 }
1500 rename-test() {
1501 # test whether missing files were renamed, generally for use with fsdiff
1502 # $1 = fsdiff output file, $2 = directory to compare to. pwd = fsdiff dir
1503 # echos non-renamed files
1504 local x y found
1505 unset sums
1506 for x in "$2"/*; do
1507 { sums+=( "$(md5sum < "$x")" ) ; } 2>/dev/null
1508 done
1509 while read -r line; do
1510 { missing_sum=$(md5sum < "$line") ; } 2>/dev/null
1511 renamed=false
1512 for x in "${sums[@]}"; do
1513 if [[ $missing_sum == "$x" ]]; then
1514 renamed=true
1515 break
1516 fi
1517 done
1518 $renamed || echo "$line"
1519 done < "$1"
1520 return 0
1521 }
1522
1523 feh() {
1524 # F = fullscren, z = random, Z = auto zoom
1525 command feh -FzZ "$@"
1526 }
1527
1528
1529
1530 fw() {
1531 firefox -P default "$@" >/dev/null 2>&1
1532 }
1533
1534 gitian() {
1535 git config user.email ian@iankelling.org
1536 }
1537
1538 # at least in flidas, things rely on gpg being gpg1
1539 gpg() {
1540 if type -P gpg2 &>/dev/null; then
1541 command gpg2 "$@"
1542 else
1543 command gpg "$@"
1544 fi
1545 }
1546
1547 gse() {
1548 local email=ian@iankelling.org
1549 git send-email --notes "--envelope-sender=<$email>" \
1550 --suppress-cc=self "$@"
1551 }
1552
1553 gup() { /a/f/gnulib/build-aux/gnupload "$@"; }
1554
1555 dejagnu() { /a/opt/dejagnu/dejagnu "$@"; }
1556
1557 hstatus() {
1558 # do git status on published repos.
1559 c /a/bin/githtml
1560 for x in *; do
1561 cd $(readlink -f $x)/..
1562 status=$(i status -s) || pwd
1563 if [[ $status ]]; then
1564 hr
1565 echo $x
1566 printf "%s\n" "$status"
1567 fi
1568 cd /a/bin/githtml
1569 done
1570 }
1571
1572 # work log
1573 wlog() {
1574 local day now i days_back
1575 days_back=${1:-16}
1576 for (( i=0; i<days_back; i++ )); do
1577 day=$( date +%F -d @$((EPOCHSECONDS - 86400*i )) )
1578 date "+%a %b %d" -d @$((EPOCHSECONDS - 86400*i )) | tr '\n' ' '
1579 /a/opt/timetrap/bin/t d -ftotal -s $day -e $day all -m '^w|lunch$'
1580 done
1581 }
1582 to() { t out -a "$@"; }
1583 ti() { t in -a "$@"; }
1584 tl() {
1585 to "$*"
1586 t s lunch
1587 t in -a "$*"
1588 m t out -a $(date +%F.%T -d @$(( $(date -d "$(echo $*|sed 's/[_.]/ /g')" +%s) + 60*45 )) )
1589 t s w
1590 }
1591
1592 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}' ; }
1593
1594 idea() {
1595 /a/opt/idea-IC-163.7743.44/bin/idea.sh "$@" &r
1596 }
1597
1598 ilogs() {
1599 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"
1600 }
1601
1602 ilog() {
1603 chan=${1:-#fsfsys}
1604 # use * instead of -r since that does sorted order
1605 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
1606 }
1607
1608 o() {
1609 if type gio &> /dev/null ; then
1610 gio open "$@"
1611 elif type gvfs-open &> /dev/null ; then
1612 gvfs-open "$@"
1613 else
1614 xdg-open "$@"
1615 fi
1616 # another alternative is run-mailcap
1617 }
1618 ccomp xdg-open o
1619
1620 # jfilter() {
1621 # grep -Evi -e "^(\S+\s+){4}(sudo|sshd|cron)\[\S*:" \
1622 # -e "^(\S+\s+){4}systemd\[\S*: (starting|started) (btrfsmaintstop|dynamicipupdate|spamd dns bug fix cronjob|rss2email)\.*$"
1623 # }
1624 # jtail() {
1625 # journalctl -n 10000 -f "$@" | jfilter
1626 # }
1627 # jr() { journalctl "$@" | jfilter | less ; }
1628 # jrf() { journalctl -n 200 -f "$@" | jfilter; }
1629
1630 jr() { journalctl "$@" ; }
1631 jrf() { journalctl -n 200 -f "$@" ; }
1632
1633
1634 ccomp journalctl jtail jr jrf
1635
1636 kff() { # keyboardio firmware flash. you must hold down the tilde key
1637 pushd /a/opt/Model01-Firmware
1638 # if we didn't want this yes hack, then remove "shell read" from
1639 # /a/opt/Kaleidoscope/etc/makefiles/sketch.mk
1640 yes $'\n' | VERBOSE=1 make flash
1641 popd
1642 }
1643
1644 wgkey() {
1645 local umask_orig name
1646 if (( $# != 1 )); then
1647 e expected 1 arg >&2
1648 return 1
1649 fi
1650 name=$1
1651 umask_orig=$(umask)
1652 umask 0077
1653 wg genkey | tee $name-priv.key | wg pubkey > $name-pub.key
1654 umask $umask_orig
1655 }
1656 wghole() {
1657 if (( $# != 2 )); then
1658 e expected 2 arg of hostname, ip suffix >&2
1659 return 1
1660 fi
1661 local host ipsuf umask_orig
1662 host=$1
1663 ipsuf=$2
1664 mkdir -p /p/c/machine_specific/$host/filesystem/etc/wireguard
1665 cd /p/c/machine_specific/$host/filesystem/etc/wireguard
1666 umask_orig=$(umask)
1667 umask 0077
1668 wg genkey | tee hole-priv.key | wg pubkey > hole-pub.key
1669 cat >wghole.conf <<EOF
1670 [Interface]
1671 # contents hole-priv.key
1672 PrivateKey = $(cat hole-priv.key)
1673 ListenPort = 1194
1674 Address = 10.8.0.$ipsuf/24
1675 # https://dev.to/tangramvision/what-they-don-t-tell-you-about-setting-up-a-wireguard-vpn-1h2g
1676 # ||: makes the systemd service not fail due to the failed command
1677 PostUp = ping -c1 10.8.0.1 ||:
1678
1679 [Peer]
1680 # li. called wgmail on that server
1681 PublicKey = CTFsje45qLAU44AbX71Vo+xFJ6rt7Cu6+vdMGyWjBjU=
1682 AllowedIPs = 10.8.0.0/24
1683 Endpoint = 72.14.176.105:1194
1684 PersistentKeepalive = 25
1685 EOF
1686 umask $umask_orig
1687 # old approach. systemd seems to work fine and cleaner.
1688 rm -f ../network/interfaces.d/wghole
1689 cedit -q $host /p/c/machine_specific/li/filesystem/etc/wireguard/wgmail.conf <<EOF || [[ $? == 1 ]]
1690 [Peer]
1691 PublicKey = $(cat hole-pub.key)
1692 AllowedIPs = 10.8.0.$ipsuf/32
1693 EOF
1694 cd - >/dev/null
1695 }
1696
1697
1698 mns() { # mount namespace
1699 ns=$1
1700 shift
1701 s mkdir -p /root/mount_namespaces
1702 if ! sudo mountpoint /root/mount_namespaces >/dev/null; then
1703 m sudo mount --bind /root/mount_namespaces /root/mount_namespaces
1704 fi
1705 m sudo mount --make-private /root/mount_namespaces
1706 if [[ ! -e /root/mount_namespaces/$ns ]]; then
1707 m sudo touch /root/mount_namespaces/$ns
1708 fi
1709 if ! sudo mountpoint /root/mount_namespaces/$ns >/dev/null; then
1710 m sudo unshare --propagation slave --mount=/root/mount_namespaces/$ns /bin/true
1711 fi
1712 m sudo -E /usr/bin/nsenter --mount=/root/mount_namespaces/$ns "$@"
1713 }
1714
1715 mnsr() { # mns run
1716 local ns=$1
1717 shift
1718 mns $ns sudo -u iank -E env "PATH=$PATH" "$@"
1719 }
1720
1721 mnsnonet() {
1722 ns=$1
1723 lomh
1724 if ! s ip netns list | grep -Fx nonet &>/dev/null; then
1725 s ip netns add nonet
1726 fi
1727 mns $ns --net=/var/run/netns/nonet sudo -E -u iank /bin/bash
1728 lomh
1729 }
1730
1731
1732 lom() {
1733 # l = the loopback device
1734 local l base
1735 if [[ $1 == /* ]]; then
1736 base=${1##*/}
1737 fs_file=$1
1738 if mns $base mountpoint -q /mnt/$base; then
1739 return 0
1740 fi
1741 l=$(losetup -j $fs_file | sed -rn 's/^([^ ]+): .*/\1/p' | head -n1 ||:)
1742 if [[ ! $l ]]; then
1743 l=$(sudo losetup -f)
1744 m sudo losetup $l $fs_file
1745 fi
1746 if ! sudo cryptsetup status /dev/mapper/$base &>/dev/null; then
1747 if ! m sudo cryptsetup luksOpen $l $base; then
1748 m sudo losetup -d $l
1749 return 1
1750 fi
1751 fi
1752 m sudo mkdir -p /mnt/$base
1753 m mns $base mount /dev/mapper/$base /mnt/$base
1754 m mns $base chown $USER:$USER /mnt/$base
1755 lomh
1756 else
1757 base=$1
1758 if mns $base mountpoint /mnt/$base &>/dev/null; then
1759 m mns $base umount /mnt/$base
1760 fi
1761 if sudo cryptsetup status /dev/mapper/$base &>/dev/null; then
1762 if ! m sudo cryptsetup luksClose /dev/mapper/$base; then
1763 echo lom: failed cryptsetup luksClose /dev/mapper/$base
1764 return 1
1765 fi
1766 fi
1767 l=$(losetup -l --noheadings | awk '$6 ~ /\/'$base'$/ {print $1}')
1768 if [[ $l ]]; then
1769 m sudo losetup -d $l
1770 else
1771 echo lom: warning: no loopback device found
1772 fi
1773 fi
1774 }
1775
1776 # mu personality. for original, just run mp. for 2, run mp 2.
1777 # this is partly duplicated in mail-setup
1778 mp() {
1779 local dead=false
1780 for s in {1..5}; do
1781 if ! killall mu; then
1782 dead=true
1783 break
1784 fi
1785 sleep 1
1786 done
1787 if ! $dead; then
1788 echo error: mu not dead
1789 m psg mu
1790 return 1
1791 fi
1792 suf=$1
1793 set -- /m/mucache ~/.cache/mu /m/.mu ~/.config/mu
1794 while (($#)); do
1795 target=$1$suf
1796 f=$2
1797 shift 2
1798 if [[ -e $f && ! -L $f ]]; then
1799 m rm -rf $f
1800 fi
1801 m ln -sf -T $target $f
1802 done
1803 }
1804
1805 # maildir enable
1806 mdenable() {
1807 local md dst ln_path src two
1808
1809 two=false
1810 case $1 in
1811 -2) two=true shift ;;
1812 esac
1813
1814 for md; do
1815 src=
1816 if $two; then
1817 dst=/m/4e2/$md
1818 else
1819 dst=/m/4e/$md
1820 fi
1821
1822 ln_path=/m/md/$md
1823 for d in /m/md/$md /m/4e2/$md; do
1824 if [[ -d $d && ! -L $d ]]; then
1825 src=$d
1826 break
1827 fi
1828 done
1829 if [[ ! $src ]]; then
1830 echo "error: could not find $md" >&2
1831 return 1
1832 fi
1833 m mv -T $src $dst
1834 m ln -sf -T $dst $ln_path
1835 done
1836 }
1837 md2enable() {
1838 mdenable -2 "$@"
1839 }
1840 mddisable() {
1841 local md=$1
1842 dst=/m/md/$md
1843
1844 ### begin copied from mdenable, but different d ###
1845 for d in /m/4e/$md /m/4e2/$md; do
1846 if [[ -d $d && ! -L $d ]]; then
1847 src=$d
1848 break
1849 fi
1850 done
1851 if [[ ! $src ]]; then
1852 echo "error: could not find $md" >&2
1853 return 1
1854 fi
1855 ### end copy from mdenable ###
1856
1857 if [[ -L $dst ]]; then m rm $dst; fi
1858 m mv -T $src $dst
1859 }
1860
1861
1862 mdt() {
1863 markdown "$1" >/tmp/mdtest.html
1864 firefox /tmp/mdtest.html
1865 }
1866
1867 mo() { xset dpms force off; } # monitor off
1868
1869 mpvgpu() {
1870 # seems to be the best gpu decoding on my nvidia 670.
1871 # vlc gets similar or better framerate, but is much darker output on my test movie at least.
1872
1873
1874 case $HOSTNAME in
1875 kd)
1876 echo 0f | sudo tee -a /sys/kernel/debug/dri/0/pstate
1877 ;;
1878 esac
1879 # going back to the default slow clock, and slower fan:
1880 # echo 07 | sudo tee -a /sys/kernel/debug/dri/0/pstate
1881 if [[ $DISPLAY ]]; then
1882 mpv --vo=vdpau --hwdec=auto "$@"
1883 else
1884 # waylandvk seems to work the same
1885 mpv --gpu-context=wayland --hwdec=auto
1886 fi
1887 }
1888
1889 mpvd() {
1890 mpv --profile=d "$@";
1891 }
1892 # mpv all media files in . or $1
1893 mpvm() {
1894 local -a extensions arg
1895 # get page source of https://en.wikipedia.org/w/index.php?title=Video_file_format&action=edit
1896 # into /a/x.log, then
1897 # grep '^| *\.' /a/x.log | sed 's/| *//;s/,//g'
1898
1899 # note: to join them together for a regex, do:
1900 # old=; for e in ${extensions[@]/./}; do if [[ ! $old ]]; then old=$e; continue; fi; echo -n "$old|"; old=$e; done; echo $e
1901 extensions=(
1902 .webm
1903 .mkv
1904 .flv
1905 .flv
1906 .vob
1907 .ogv .ogg
1908 .drc
1909 .gif
1910 .gifv
1911 .mng
1912 .avi
1913 .MTS .M2TS .TS
1914 .mov .qt
1915 .wmv
1916 .yuv
1917 .rm
1918 .rmvb
1919 .viv
1920 .asf
1921 .amv
1922 .mp4 .m4p .m4v
1923 .mpg .mp2 .mpeg .mpe .mpv
1924 .mpg .mpeg .m2v
1925 .m4v
1926 .svi
1927 .3gp
1928 .3g2
1929 .mxf
1930 .roq
1931 .nsv
1932 )
1933 arg=("(" -iname "*${extensions[0]}")
1934 for (( i=1 ; i < ${#extensions[@]}; i++ )); do
1935 arg+=(-o -iname "*${extensions[i]}")
1936 done
1937 arg+=(")")
1938 dir=${1:-.}
1939 # debug:
1940 #find $dir "${arg[@]}" -size +200k
1941 find $dir "${arg[@]}" -size +200k -exec mpv --profile=d '{}' +
1942 }
1943 mpvs() {
1944 mpv --profile=s "$@";
1945 }
1946
1947 myirc() {
1948 if [[ ! $1 ]]; then
1949 set -- fsf-office
1950 fi
1951 local d1 d2
1952 d=( /var/lib/znc/moddata/log/iank/{freenode,libera} )
1953 # use * instead of -r since that does sorted order
1954 ssh root@iankelling.org "for f in ${d[@]}; do cd \$f/#$1; grep '\<iank.*' *; done" | cut --complement -c12-16
1955 }
1956 mypidgin() {
1957 c /p/c/.purple/logs/jabber/iank@fsf.org/office@conference.fsf.org.chat
1958 for x in *.html; do html2text -o ${x%.html}.txt $x; done;
1959 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}'
1960 }
1961 allmyirc() {
1962 local d
1963 d=/var/lib/znc/moddata/log/iank/freenode
1964 ssh root@iankelling.org "cd $d; find . -mtime -60 -type f -exec grep '\<iank.*' {} +" | sed -r 's,^..([^/]*)/(.{11})(.{5})(.{8}).,\2\4 \1,' | sort
1965 }
1966
1967 # usage: debvm DEBIAN_VERSION RAM_MB
1968 debvm() {
1969 local ver ram fname src
1970 ver=$1
1971 ram=${2:-2024}
1972 # * is because it might have -backports in the name
1973 fname=debian-$ver-*nocloud-$(dpkg --print-architecture).qcow2
1974 src=/a/opt/roms/$fname
1975 if [[ ! -f $src ]]; then
1976 echo debvm: not found $src, download from eg: https://cloud.debian.org/images/cloud/buster/latest/
1977 return 1
1978 fi
1979 cp -a $src /t
1980 # note, in fai-revm we do this: not sure why, maybe because of br device
1981 # --graphics spice,listen=0.0.0.0
1982 m s virt-install --osinfo debian11 --rng /dev/urandom -n deb${ver}tmp --import -r $ram --vcpus 2 --disk /t/$fname --graphics spice
1983 # note: to ssh into this machine will require host key generation: ssh-keygen -A
1984
1985 # random: for cvs2git on gnu www, use debian 10. I could use trisquel,
1986 # but happen to want to try out the debian cloud images. the upstream
1987 # requires python2 and hasn't really changed since the version in d10.
1988 #
1989 # apt install cvs2git cvs
1990 # # 7G was not enough
1991 # mount -o mode=1777,nosuid,nodev,size=34G -t tmpfs tmpfs /tmp
1992 # cvs2git --encoding utf_8 --fallback-encoding ascii --dumpfile=dump www-rsync/www |& tee /tmp/l
1993 ## www-rsync is an rsynced copy of the cvsfrom savannah
1994 }
1995
1996 mygajim() {
1997 local time time_sec time_pretty days
1998 days=${1:-16}
1999 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
2000 case $time in
2001 16*) : ;;
2002 *) continue ;;
2003 esac
2004 if ! time_pretty=$(date +%F.%R -d @$time); then
2005 echo bad time: $time
2006 return 1
2007 fi
2008 echo $time_pretty "$l"
2009 time_sec=${time%%.*}
2010 # only look at the last 18 days. generally just use this for timesheet.
2011 if (( time_sec < EPOCHSECONDS - 60 * 60 * 24 * days )); then break; fi
2012 done
2013 }
2014
2015 allmygajim() {
2016 sqlite3 -separator ' ' /p/c/subdir_files/.local/share/gajim/logs.db "select time, message from logs where contact_name = 'iank'" | less
2017 }
2018
2019 gajlogs() {
2020 sqlite3 -separator ' ' /p/c/subdir_files/.local/share/gajim/logs.db "select time, message from logs" | less
2021 }
2022
2023
2024 net-dev-info() {
2025 e "lspci -nnk|gr -iA2 net"
2026 lspci -nnk|gr -iA2 net
2027 hr
2028 e "s lshw -C network"
2029 hr
2030 sudo lshw -C network
2031 }
2032
2033 nk() {
2034 ser stop NetworkManager
2035 ser disable NetworkManager
2036 ser stop NetworkManager-wait-online.service
2037 ser disable NetworkManager-wait-online.service
2038 ser stop dnsmasq
2039 sudo resolvconf -d NetworkManager
2040 # ser start dnsmasq
2041 sudo ifup br0
2042 }
2043 ngo() {
2044 sudo ifdown br0
2045 ser start NetworkManager
2046 sleep 4
2047 sudo nmtui-connect
2048 }
2049
2050 otp() {
2051 oathtool --totp -b "$*" | xclip -selection clipboard
2052 }
2053 j() {
2054 "$@" |& pee "xclip -r -selection clipboard"
2055 }
2056
2057
2058 pakaraoke() {
2059 # from http://askubuntu.com/questions/456021/remove-vocals-from-mp3-and-get-only-instrumentals
2060 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
2061 }
2062
2063 pfind() { #find *$1* in $PATH
2064 [[ $# != 1 ]] && { echo requires 1 argument; return 1; }
2065 local pathArray
2066 IFS=: pathArray=($PATH); unset IFS
2067 find "${pathArray[@]}" -iname "*$1*"
2068 }
2069
2070 pick-trash() {
2071 # trash-restore lists everything that has been trashed at or below CWD
2072 # This picks out files just in CWD, not subdirectories,
2073 # which also match grep $1, usually use $1 for a time string
2074 # which you get from running restore-trash once first
2075 local name x ask
2076 local nth=1
2077 # last condition is to not ask again for ones we skipped
2078 while name="$( echo | restore-trash | gr "$PWD/[^/]\+$" | gr "$1" )" \
2079 && [[ $name ]] && (( $(wc -l <<<"$name") >= nth )); do
2080 name="$(echo "$name" | head -n $nth | tail -n 1 )"
2081 read -r -p "$name [Y/n] " ask
2082 if [[ ! $ask || $ask == [Yy] ]]; then
2083 x=$( echo "$name" | gr -o "^\s*[0-9]*" )
2084 echo $x | restore-trash > /dev/null
2085 elif [[ $ask == [Nn] ]]; then
2086 nth=$((nth+1))
2087 else
2088 return
2089 fi
2090 done
2091 }
2092
2093
2094 pub() {
2095 rld /a/h/_site/ li:/var/www/iankelling.org/html
2096 }
2097
2098
2099 pumpa() {
2100 # fixes the menu bar in xmonad. this won\'t be needed when xmonad
2101 # packages catches up on some changes in future (this is written in
2102 # 4/2017)
2103 #
2104 # geekosaur: so youll want to upgrade to xmonad 0.13 or else use a
2105 # locally modified XMonad.Hooks.ManageDocks that doesnt set the
2106 # work area; turns out it\'s impossible to set correctly if you are
2107 # not a fully EWMH compliant desktop environment
2108 #
2109 # geekosaur: chrome shows one failure mode, qt/kde another, other
2110 # gtk apps a third, ... I came up with a setting that works for me
2111 # locally but apparently doesnt work for others, so we joined the
2112 # other tiling window managers in giving up on setting it at all
2113 #
2114 xprop -root -remove _NET_WORKAREA
2115 command pumpa & r
2116 }
2117
2118 # reviewboard, used at my old job
2119 #rbpipe() { rbt post -o --diff-filename=- "$@"; }
2120 #rbp() { rbt post -o "$@"; }
2121
2122 rebr() {
2123 sudo ifdown br0
2124 sudo ifup br0
2125 }
2126
2127
2128 r2e() { command r2e -d /p/c/rss2email.json -c /p/c/rss2email.cfg "$@"; }
2129 # only run on MAIL_HOST. simpler to keep this on one system.
2130 r2eadd() { # usage: name url
2131 # initial setup of rss2email:
2132 # r2e new r2e@iankelling.org
2133 # that initializes files, and sets default email.
2134 # symlink to the config doesnt work, so I copied it to /p/c
2135 # and then use cli option to specify explicit path.
2136 # Only option changed from default config is to set
2137 # force-from = True
2138 #
2139 # or else for a few feeds, the from address is set by the feed, and
2140 # if I fail delivery, then I send a bounce message to that from
2141 # address, which makes me be a spammer.
2142
2143 r2e add $1 "$2" $1@r2e.iankelling.org
2144 # get up to date and dont send old entries now:
2145 r2e run --no-send $1
2146 }
2147
2148 rspicy() { # usage: HOST DOMAIN
2149 # connect to spice vm remote host. use vspicy for local host
2150 local port
2151 # shellcheck disable=SC2087
2152 port=$(ssh $1<<EOF
2153 sudo virsh dumpxml $2|grep "<graphics.*type='spice'" | \
2154 sed -rn "s/.*port='([0-9]+).*/\1/p"
2155 EOF
2156 )
2157 if [[ $port ]]; then
2158 spicy -h $1 -p $port
2159 else
2160 echo "error: no port found. check that the domain is running."
2161 fi
2162 }
2163
2164
2165 scssl() {
2166 # s gem install scss-lint
2167 pushd /a/opt/thoughtbot-guides
2168 git pull --stat
2169 popd
2170 scss-lint -c /a/opt/thoughtbot-guides/style/sass/.scss-lint.yml "$@"
2171 }
2172
2173 skbrc() {
2174 sk -e 2120,245 /b/ds/brc /b/ds/brc2
2175 }
2176
2177 skaraoke() {
2178 local tmp out
2179 out=${2:-${1%.*}.sh}
2180 tmp=$(mktemp -d)
2181 script -t -c "mpv --no-config --no-resume-playback --no-terminal --no-audio-display '$1'" $tmp/typescript 2>$tmp/timing
2182 # todo, the current sleep seems pretty good, but it
2183 # would be nice to have an empirical measurement, or
2184 # some better wait to sync up.
2185 #
2186 # note: --loop-file=no prevents it from hanging if you have that
2187 # set to inf the mpv config.
2188 # --loop=no prevents it from exit code 3 due to stdin if you
2189 # had it set to inf in mpv config.
2190 #
2191 # args go to mpv, for example --volume=80, 50%
2192 cat >$out <<EOFOUTER
2193 #!/bin/bash
2194 trap "trap - TERM && kill 0" INT TERM ERR; set -e
2195 ( sleep .2; scriptreplay <( cat <<'EOF'
2196 $(cat $tmp/timing)
2197 EOF
2198 ) <( cat <<'EOF'
2199 $(cat $tmp/typescript)
2200 EOF
2201 ))&
2202 base64 -d - <<'EOF'| mpv --loop=no --loop-file=no --no-terminal --no-audio-display "\$@" -
2203 $(base64 "$1")
2204 EOF
2205 kill 0
2206 EOFOUTER
2207 rm -r $tmp
2208 chmod +x $out
2209 }
2210
2211 smeld() { # ssh meld usage host1 host2 file
2212 meld <(ssh $1 cat $3) <(ssh $2 cat $3)
2213 }
2214
2215 spd() {
2216 PATH=/usr/local/spdhackfix:$PATH command spd "$@"
2217 }
2218
2219 spamf() { # spamtest on FILE
2220 local spamcpre spamdpid
2221
2222 if (( $# != 1 )); then
2223 e spamtest error: expected 1 arg, filename >&2
2224 return 1
2225 fi
2226
2227 spamdpid=$(systemctl status spamassassin| sed -n '/^ *Main PID:/s/[^0-9]//gp')
2228 spamcpre="nsenter -t $spamdpid -n -m"
2229 s $spamcpre sudo -u Debian-exim spamassassin -t --cf='score PYZOR_CHECK 0' <"$1"
2230 }
2231
2232
2233 # mail related
2234 testmail() {
2235 declare -gi _seq; _seq+=1
2236 echo "test body" | m mail -s "test mail from $HOSTNAME, $_seq" "${@:-root@localhost}"
2237 # for testing to send from an external address, you can do for example
2238 # -fian@iank.bid -aFrom:ian@iank.bid web-6fnbs@mail-tester.com
2239 # note in exim, you can retry a deferred message
2240 # s exim -M MSG_ID
2241 # MSG_ID is in /var/log/exim4/mainlog, looks like 1ccdnD-0001nh-EN
2242 }
2243
2244 # to test sieve, use below command. for fsf mail, see offlineimap-sync script
2245 # make modifications, then copy to live file, use -eW to actually modify mailbox
2246 #
2247 # Another option is to use sieve-test SCRIPT MAIL_FILE. note,
2248 # sieve-test doesnt know about envelopes, Im not sure if sieve-filter does.
2249
2250 # sieve with output filter. arg is mailbox, like INBOX.
2251 # This depends on dovecot conf, notably mail_location in /etc/dovecot/conf.d/10-mail.conf
2252
2253 # always run this first, edit the test files, then run the following
2254 testsieve() {
2255 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
2256 }
2257 runsieve() {
2258 c ~/sieve; cp personal{test,}.sieve; cp lists{test,}.sieve; cp personalend{test,}.sieve
2259 sieve-filter -eWv ~/sieve/maintest.sieve ${1:-INBOX} delete &> /tmp/testsieve.log
2260 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
2261 }
2262
2263 # usage:
2264 # alertme SUBJECT
2265 # printf "subject\nbody\n" | alertme
2266 alertme() {
2267 if [[ -t 0 ]]; then
2268 exim -t <<EOF
2269 From: alertme@b8.nz
2270 To: alerts@iankelling.org
2271 Subject: $*
2272 EOF
2273 else
2274 read sub
2275 { cat <<EOF
2276 From: alertme@b8.nz
2277 To: alerts@iankelling.org
2278 Subject: $sub
2279
2280 EOF
2281 cat
2282 } | exim -t
2283 fi
2284 }
2285 daylertme() {
2286 if [[ -t 0 ]]; then
2287 exim -t <<EOF
2288 From: alertme@b8.nz
2289 To: daylert@iankelling.org
2290 Subject: $*
2291 EOF
2292 else
2293 read sub
2294 { cat <<EOF
2295 From: alertme@b8.nz
2296 To: daylert@iankelling.org
2297 Subject: $sub
2298
2299 EOF
2300 cat
2301 } | exim -t
2302 fi
2303 }
2304
2305 # alert when a page goes live.
2306 alert200() {
2307 local quiet url tmpdir
2308 quiet=false
2309 case $1 in
2310 # dont send a diff of the html. some html is not very readable
2311 -q) quiet=true
2312 shift
2313 ;;
2314 esac
2315 url="$1"
2316 tmpdir="$(mktemp -d)"
2317 cd $tmpdir
2318 while true; do
2319 if wget -q "$url"; then
2320 if $quiet; then
2321 echo | daylert 200
2322 else
2323 alertme $tmpdir
2324 fi
2325 fi
2326 sleep $(( 120 + RANDOM % 300 ))
2327 done
2328 }
2329
2330 # alert on changes to a webpage (just the base page that curl gets)
2331 # usage: weblert URL [SUBJECT...]
2332 weblert() {
2333 local u old new quiet
2334 quiet=false
2335 case $1 in
2336 # dont send a diff of the html. some html is not very readable
2337 -q) quiet=true
2338 shift
2339 ;;
2340 esac
2341 u="$1"
2342 shift
2343 subject="${*:-weblert}"
2344 old=$(curl -s "$u") ||:
2345 while true; do
2346 new=$(curl -s "$u") ||:
2347 if [[ $old && $new ]]; then
2348 if [[ $new != "$old" ]]; then
2349 if $quiet; then
2350 echo | daylertme "$subject"
2351 else
2352 diff <(printf "%s\n" "$old") <(printf "%s\n" "$new") | daylertme "$subject" ||:
2353 fi
2354 fi
2355 old="$new"
2356 fi
2357 sleep $(( 60 + RANDOM % 120 ))
2358 done
2359 }
2360
2361 torshell() {
2362 # per man torsocks
2363 source `type -p torsocks` on
2364 }
2365
2366 eless2() {
2367 less /var/log/exim4/mymain
2368 }
2369
2370
2371 # mail related
2372 testexim() {
2373 # testmail above calls sendmail, which is a link to exim/postfix.
2374 # its docs dont say a way of adding an argument
2375 # to sendmail to turn on debug output. We could make a wrapper, but
2376 # that is a pain. Exim debug args are documented here:
2377 # http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
2378 #
2379 # http://www.exim.org/exim-html-current/doc/html/spec_html/ch-building_and_installing_exim.html
2380 # note, for exim daemon, you can turn on debug options by
2381 # adding -d, etc to COMMONOPTIONS in
2382 # /etc/default/exim4
2383 #
2384 # to specify recipients other than those in to, cc, bcc, you can use the cli args, eg:
2385 # exim -t 'test@zroe.org, t2@zroe.org' <<'EOF'
2386 #
2387 # -t = get recipient from header
2388 exim -d -t <<EOF
2389 From: root@$(hostname -f)
2390 To: root@$(hostname -f)
2391 Subject: test2
2392
2393 This is a test message.
2394 EOF
2395 }
2396
2397 # test bounce exim
2398 testbexim() {
2399 to=$1
2400 exim -d -f '<>' $to <<EOF
2401 From: Mail Delivery System <Mailer-Daemon@gnu.org>
2402 To: $to
2403 Subject: Mail delivery failed: returning message to sender
2404
2405 This message was created automatically by mail delivery software.
2406 EOF
2407
2408 }
2409
2410
2411 # toggle keyboard
2412 tk() {
2413 # based on
2414 # https://askubuntu.com/questions/160945/is-there-a-way-to-disable-a-laptops-internal-keyboard
2415 id=$(xinput --list --id-only 'AT Translated Set 2 keyboard')
2416 if xinput list | grep -F '∼ AT Translated Set 2 keyboard' &>/dev/null; then
2417 echo enabling keyboard
2418 # find the first slave keyboard number, they are all the same in my output.
2419 # if they werent, worst case we would need to save the slave number somewhere
2420 # when it got disabled.
2421 slave=$(xinput list | sed -n 's/.*slave \+keyboard (\([0-9]*\)).*/\1/p' | head -n1)
2422 xinput reattach $id $slave
2423 else
2424 xinput float $id
2425 fi
2426 }
2427
2428 tm() {
2429 # timer in minutes
2430 # --no-config
2431 (sleep $(calc "$* * 60") && mpv --no-config --volume 50 /a/bin/data/alarm.mp3) > /dev/null 2>&1 &
2432 }
2433
2434 trg() { transmission-remote-gtk & r; }
2435 trc() {
2436 # example, set global upload limit to 100 kilobytes:
2437 # trc -u 100
2438 TR_AUTH=":$(jq -r .profiles[0].password ~/.config/transmission-remote-gtk/config.json)" transmission-remote transmission.lan -ne "$@"
2439 }
2440
2441 trysleep() {
2442 retries="$1"
2443 sleepsecs="$2"
2444 shift 2
2445 for (( i=0; i < retries - 1; i++ )); do
2446 if "$@"; then
2447 return 0
2448 fi
2449 sleep $sleepsecs
2450 done
2451 "$@"
2452 }
2453
2454
2455 tu() {
2456 local s
2457 if [[ -e $1 && ! -w $1 || ! -w $(dirname "$1") ]]; then
2458 s=s;
2459 fi
2460 # full path for using in some initial setup steps
2461 $s /a/exe/teeu "$@"
2462 }
2463
2464 enn() {
2465 local ecmd pid
2466
2467 ecmd="/usr/sbin/exim4 -C /etc/exim4/my.conf"
2468 if ip a show veth1-mail &>/dev/null; then
2469 s $ecmd "$@"
2470 return
2471 fi
2472 pid=$(pgrep -f "/usr/sbin/exim4 -bd -q30m -C /etc/exim4/my.conf"|h1)
2473 m s nsenter -t $pid -n -m $ecmd "$@"
2474 }
2475
2476 # get pid of systemd service
2477 servicepid() {
2478 local pid unit dir
2479 unit="$1"
2480 pid=$(systemctl show --property MainPID --value "$unit")
2481 case $pid in
2482 [1-9]*) : ;;
2483 *)
2484
2485 dir=/sys/fs/cgroup/system.slice
2486 if [[ ! -d $dir ]]; then
2487 # t10 and older directory.
2488 dir=/sys/fs/cgroup/systemd/system.slice
2489 fi
2490
2491 # 0 or empty. This file includes the MainPid, so I expect we
2492 # could just get this in the first place, but i don't know if that
2493 # is always the case.
2494 pid=$(head -n1 $dir/${unit%.service}.service/cgroup.procs)
2495 ;;
2496 esac
2497 if [[ $pid ]]; then
2498 printf "%s\n" "$pid"
2499 else
2500 return 1
2501 fi
2502 }
2503
2504 sdnbash() { # systemd namespace bash
2505 local unit pid
2506 if (( $# != 1 )); then
2507 echo $0: error wrong number of args >&2
2508 return 1
2509 fi
2510 unit=$1
2511 pid=$(servicepid $unit)
2512 m sudo nsenter -t $pid -n -m sudo -u $USER -i bash
2513 }
2514
2515 sdnbashroot() { # systemd namespace bash
2516 local unit pid
2517 if (( $# != 1 )); then
2518 echo $0: error wrong number of args >&2
2519 return 1
2520 fi
2521 unit=$1
2522 pid=$(servicepid $unit)
2523 m sudo nsenter -t $pid -n -m bash
2524 }
2525
2526
2527 sdncmd() { # systemd namespace cmd
2528 local unit pid
2529 if (( $# <= 2 )); then
2530 echo $0: error wrong number of args >&2
2531 return 1
2532 fi
2533 unit=$1
2534 shift
2535 pid=$(servicepid $unit)
2536 m sudo nsenter -t $pid -n -m sudo -u $USER -i "$@"
2537 }
2538
2539
2540 mailnnbash() {
2541 sdnbash mailnn
2542 }
2543
2544 # we use wireguard now, use mailnnbash.
2545 # mailvpnbash() {
2546 # m sudo nsenter -t $(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/.*mail.conf") -n -m sudo -u $USER -i bash
2547 # }
2548
2549 eximbash() {
2550 local pid
2551 pid=$(pgrep -f "/usr/sbin/exim4 -bd -q30m -C /etc/exim4/my.conf"|h1)
2552 if [[ ! $pid ]]; then
2553 echo "eximbash: failed to find exim pid. systemctl -n 30 status exim4:"
2554 systemctl status exim4
2555 fi
2556 m sudo nsenter -t $pid -n -m
2557 }
2558 spamnn() {
2559 local spamdpid
2560 spamdpid=$(systemctl show --property MainPID --value spamassassin)
2561 m sudo nsenter -t $spamdpid -n -m sudo -u Debian-exim spamassassin "$@"
2562 }
2563 unboundbash() {
2564 m sudo nsenter -t $(systemctl status unbound| sed -n '/^ *Main PID:/s/[^0-9]//gp') -n -m sudo -u $USER -i bash
2565 }
2566
2567 nmtc() {
2568 s nmtui-connect "$@"
2569 }
2570
2571 mailnncheck() {
2572 local unit pid ns mailnn
2573 # mailvpn would belong on the list if using openvpn
2574 for unit in mailnn unbound dovecot spamassassin exim4 radicale; do
2575 pid=$(servicepid $unit)
2576 echo debug: unit=$unit pid=$pid
2577 if [[ ! $pid ]]; then
2578 echo failed to find pid for unit=$unit
2579 continue
2580 fi
2581 if ! ns=$(s readlink /proc/$pid/ns/net); then
2582 echo failed to find ns for unit=$unit pid=$pid
2583 continue
2584 fi
2585 if [[ $mailnn ]]; then
2586 if [[ $ns != "$mailnn" ]]; then
2587 echo "$unit ns $ns != $mailnn"
2588 fi
2589 else
2590 mailnn=$ns
2591 fi
2592 done
2593
2594 }
2595
2596
2597 vpncmd() {
2598 m sudo -E env "PATH=$PATH" nsenter -t $(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/.*client.conf") -n "$@"
2599 }
2600
2601 vpni() {
2602 vpncmd sudo -u iank env "PATH=$PATH" "$@"
2603 }
2604 vpnbash() {
2605 vpncmd bash
2606 }
2607
2608
2609 vpn() {
2610 if [[ -e /lib/systemd/system/openvpn-client@.service ]]; then
2611 local vpn_service=openvpn-client
2612 else
2613 local vpn_service=openvpn
2614 fi
2615
2616 [[ $1 ]] || { echo need arg; return 1; }
2617 journalctl --unit=$vpn_service@$1 -f -n0 &
2618 # sometimes the journal doesnt open until after the vpn output
2619 # has happened. hoping this fixes that.
2620 sleep 1
2621 sudo systemctl start $vpn_service@$1
2622 # sometimes the ask-password agent does not work and needs a delay.
2623 sleep .5
2624 # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=779240
2625 # noticed around 8-2017 after update from around stretch release
2626 # on debian testing, even though the bug is much older.
2627 sudo systemd-tty-ask-password-agent
2628 }
2629
2630 fixu() {
2631 local stats
2632 ls -lad /run/user/1000
2633 stats=$(stat -c%a-%g-%u /run/user/1000)
2634 if [[ $stats != 700-1000-1000 ]]; then
2635 m s chmod 700 /run/user/1000; m s chown iank.iank /run/user/1000
2636 fi
2637 }
2638
2639 # systemctl is-enabled / status / cat says nothing, instead theres
2640 # some obscure symlink. paths copied from man systemd.unit.
2641 # possibly also usefull, but incomplete, doesnt show units not loaded in memory:
2642 # seru list-dependencies --reverse --all UNIT
2643 sysd-deps() {
2644 local f
2645 local -a dirs search
2646 ngset
2647
2648 case $1 in
2649 u)
2650 search=(
2651 ~/.config/systemd/user.control/*
2652 $XDG_RUNTIME_DIR/systemd/user.control/*
2653 $XDG_RUNTIME_DIR/systemd/transient/*
2654 $XDG_RUNTIME_DIR/systemd/generator.early/*
2655 ~/.config/systemd/user/*
2656 /etc/systemd/user/*
2657 $XDG_RUNTIME_DIR/systemd/user/*
2658 /run/systemd/user/*
2659 $XDG_RUNTIME_DIR/systemd/generator/*
2660 ~/.local/share/systemd/user/*
2661 /usr/lib/systemd/user/*
2662 $XDG_RUNTIME_DIR/systemd/generator.late/*
2663 )
2664 ;;
2665 *)
2666 search=(
2667 /etc/systemd/system.control/*
2668 /run/systemd/system.control/*
2669 /run/systemd/transient/*
2670 /run/systemd/generator.early/*
2671 /etc/systemd/system/*
2672 /etc/systemd/systemd.attached/*
2673 /run/systemd/system/*
2674 /run/systemd/systemd.attached/*
2675 /run/systemd/generator/*
2676 /lib/systemd/system/*
2677 /run/systemd/generator.late/*
2678 )
2679 ;;
2680 esac
2681 for f in "${search[@]}"; do
2682 [[ -d $f ]] || continue
2683 case $f in
2684 *.requires|*.wants)
2685 dirs+=("$f")
2686 ;;
2687 esac
2688 done
2689 # dirs is just so we write out the directory names, ls does it when there is 2 or more dirs.
2690 case ${#dirs[@]} in
2691 1)
2692 echo "${dirs[0]}:"
2693 ll "${dirs[@]}"
2694 ;;
2695 0) : ;;
2696 *)
2697 ll "${dirs[@]}"
2698 ;;
2699 esac
2700 ngreset
2701 }
2702
2703 fixvpndns() {
2704 local link istls
2705 read _ link _ istls < <(resolvectl dnsovertls tunfsf)
2706 case $istls in
2707 yes|no) : ;;
2708 *) echo fixvpndns error: unexpected istls value: $istls >&2; return 1 ;;
2709 esac
2710 s busctl call org.freedesktop.resolve1 /org/freedesktop/resolve1 org.freedesktop.resolve1.Manager SetLinkDNSOverTLS is $link no
2711 }
2712
2713 vpnoff() {
2714 [[ $1 ]] || { echo need arg; return 1; }
2715 if [[ -e /lib/systemd/system/openvpn-client@.service ]]; then
2716 local vpn_service=openvpn-client
2717 else
2718 local vpn_service=openvpn
2719 fi
2720 sudo systemctl stop $vpn_service@$1
2721 }
2722 vpnoffc() { # vpn off client
2723 ser stop openvpn-client-tr@client
2724 }
2725 vpnc() {
2726 ser start openvpn-client-tr@client
2727 }
2728
2729
2730 vspicy() { # usage: VIRSH_DOMAIN
2731 # connect to vms made with virt-install
2732 spicy -p $(sudo virsh dumpxml "$1"|grep "<graphics.*type='spice'"|\
2733 sed -r "s/.*port='([0-9]+).*/\1/")
2734 }
2735
2736 wian() {
2737 cat-new-files /m/4e/INBOX/new
2738 }
2739
2740 wtr() { curl wttr.in/boston; }
2741
2742 xevkb() { xev -event keyboard; }
2743
2744 # * misc stuff
2745
2746 vrun() {
2747 printf "running: %s\n" "$*"
2748 "$@"
2749 }
2750
2751 f=/a/f/ansible-configs/files/common/etc/fsf-workstation-bashrc.sh
2752 if [[ -e $f ]]; then
2753 # shellcheck disable=SC1090
2754 source $f
2755 fi
2756
2757 electrum() {
2758 # https://electrum.readthedocs.io/en/latest/tor.html
2759 # https://github.com/spesmilo/electrum-docs/issues/129
2760 s rsync -ptog --chown bitcoin:bitcoin ~/.Xauthority /var/lib/bitcoind/.Xauthority
2761 sudo -u bitcoin DISPLAY=$DISPLAY XAUTHORITY=/var/lib/bitcoind/.Xauthority /a/opt/electrum-4.2.1-x86_64.AppImage -p socks5:localhost:9050
2762 }
2763 monero() {
2764 sudo -u bitcoin DISPLAY=$DISPLAY XAUTHORITY=/var/lib/bitcoind/.Xauthority /a/opt/monero-gui-v0.17.3.2/monero-wallet-gui
2765 }
2766
2767
2768 reset-konsole() {
2769 # we also have a file in /a/c/...konsole...
2770 local f=$HOME/.config/konsolerc
2771 setini DefaultProfile profileian.profile "Desktop Entry" $f
2772 setini Favorites profileian.profile "Favorite Profiles" $f
2773 setini ShowMenuBarByDefault false KonsoleWindow $f
2774 setini TabBarPosition Top TabBar $f
2775 }
2776
2777 reset-sakura() {
2778 while read -r k v; do
2779 # shellcheck disable=SC2154
2780 setini $k $v sakura /a/c/subdir_files/.config/sakura/sakura.conf
2781 done <<'EOF'
2782 colorset1_back rgb(33,37,39)
2783 less_questions true
2784 audible_bell No
2785 visible_bell No
2786 disable_numbered_tabswitch true
2787 scroll_lines 10000000
2788 scrollbar true
2789 EOF
2790 }
2791
2792 # make a page of links found in the files $@. redirect output
2793 linkhtml() {
2794 gr -oh 'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)' "$@" | \
2795 rev | sort -u | rev | sed 's,.*,<a href="\0">\0</a><br\>,'
2796 }
2797
2798 reset-xscreensaver() {
2799 # except for spash, i set these by setting gui options in
2800 # xscreensaver-command -demo
2801 # then finding the corresponding option in .xscreensaver
2802 # spash, i happened to notice in .xscreensaver
2803 #
2804 # dpmsOff, monitor doesnt come back on using old free software supported nvidia card
2805 cat > /home/iank/.xscreensaver <<'EOF'
2806 mode: blank
2807 dpmsEnabled: True
2808 dpmsStandby: 0:07:00
2809 dpmsSuspend: 0:08:00
2810 dpmsOff: 0:00:00
2811 timeout: 0:05:00
2812 lock: True
2813 lockTimeout: 0:06:00
2814 splash: False
2815 EOF
2816
2817 }
2818
2819
2820 # very useful, copy directory structure 3 deep. add remove /*/ to change level
2821 # rsync -aivh --exclude '/*/*/*/' -f"+ */" -f"- *" SRC DEST
2822
2823
2824 # * stuff that makes sense to be at the end
2825 if [[ "$SUDOD" ]]; then
2826 # allow failure, for example if we are sudoing into a user with diffferent/lesser permissions.
2827 cd "$SUDOD" ||:
2828 unset SUDOD
2829 elif [[ -d /a ]] && [[ $PWD == "$HOME" ]] && [[ $- == *i* ]]; then
2830 cd /a
2831 OLDPWD=
2832 fi
2833
2834
2835
2836
2837 # for mitmproxy to get a newer python.
2838 # commented until i want to use it because it
2839 # noticably slows bash startup
2840 #
2841
2842 mypyenvinit () {
2843 if [[ $EUID == 0 || ! -e ~/.pyenv/bin ]]; then
2844 echo "error: dont be root. make sure pyenv is installed"
2845 return 1
2846 fi
2847 export PATH="$HOME/.pyenv/bin:$PATH"
2848 eval "$(pyenv init -)"
2849 eval "$(pyenv virtualenv-init -)"
2850 }
2851
2852
2853 export GOPATH=$HOME/go
2854 path-add $GOPATH/bin
2855 path-add /usr/local/go/bin
2856
2857 # I have the git repo and a release. either one should work.
2858 # I have both because I was trying to solve an issue that
2859 # turned out to be unrelated.
2860 # ARDUINO_PATH=/a/opt/Arduino/build/linux/work
2861
2862 ## i should have documented this...
2863 # based on https://github.com/keyboardio/Kaleidoscope
2864 export KALEIDOSCOPE_DIR=/a/opt/Kaleidoscope
2865
2866 # They want to be added to the start, but i think
2867 # that should be avoided unless we really need it.
2868 path-add --end ~/.npm-global
2869
2870
2871 path-add --end $HOME/.cargo/bin
2872
2873 if type -P rg &>/dev/null; then
2874 # --no-messages because of annoying errors on broken symlinks
2875 # -z = search .gz etc files
2876 # -. = search dotfilesq
2877 rg() { command rg -. -z --no-messages -L -i -M 900 --no-ignore-parent --no-ignore-vcs -g '!.git' -g '!auto-save-list' -g '!.savehist' "$@" || return $?; }
2878 #fails if not exist. ignore
2879 complete -r rg 2>/dev/null ||:
2880 else
2881 alias rg=grr
2882 fi
2883
2884
2885
2886 # taken from default changes to bashrc and bash_profile
2887 path-add --end --ifexists $HOME/.rvm/bin
2888 # also had ruby bin dir, but moved that to environment.sh
2889 # so its included in overall env
2890
2891
2892 export BASEFILE_DIR=/a/bin/fai-basefiles
2893
2894 #export ANDROID_HOME=/a/opt/android-home
2895 # https://f-droid.org/en/docs/Installing_the_Server_and_Repo_Tools/
2896 #export USE_SDK_WRAPPER=yes
2897 #PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools
2898
2899 # didnt get drush working, if I did, this seems like the
2900 # only good thing to include for it.
2901 # Include Drush completion.
2902 # if [ -f "/home/ian/.drush/drush.complete.sh" ] ; then
2903 # source /home/ian/.drush/drush.complete.sh
2904 # fi
2905
2906
2907 # best practice
2908 unset IFS
2909
2910 # https://wiki.archlinux.org/index.php/Xinitrc#Autostart_X_at_login
2911 # i added an extra condition as gentoo xorg guide says depending on
2912 # $DISPLAY is fragile.
2913 if [[ ! $DISPLAY && $XDG_VTNR == 1 ]] && shopt -q login_shell && isarch; then
2914 exec startx
2915 fi
2916
2917
2918 # ensure no bad programs appending to this file will have an affect
2919 return 0