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