X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;ds=sidebyside;f=brc2;h=e936badfba828a52dcbab2c003e8b1c05bd2c65a;hb=refs%2Fheads%2Fmaster;hp=5f0a5ff45f5ee11430b1d232c6fed78473686b2a;hpb=5a6e794f459b3e83b6d665e05d0ab0c8bc6cdfbe;p=distro-setup diff --git a/brc2 b/brc2 index 5f0a5ff..fe5d60f 100644 --- a/brc2 +++ b/brc2 @@ -1,6 +1,25 @@ #!/bin/bash -# Copyright (C) 2019 Ian Kelling -# SPDX-License-Identifier: AGPL-3.0-or-later +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to switch +# its license to GPL. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + # this gets sourced. shebang is just for file mode detection @@ -10,9 +29,29 @@ if [[ $LESSHISTFILE == - ]]; then HISTFILE= c() { cd "$@"; } elif [[ $HISTFILE ]]; then - HISTFILE=$HOME/.bh + # use an alternate history file when we are streaming. + if [[ -e $HOME/.iank-stream-on ]]; then + HISTFILE=/a/bin/data/stream_hist + else + HISTFILE=$HOME/.bh + fi fi +# history personal +hip() { + history -c + HISTFILE=$HOME/.bh + history -r +} + +# history for streaming +his() { + history -c + HISTFILE=/a/bin/data/stream_hist + history -r +} + + source /a/bin/distro-setup/path-add-function path-add /a/exe # add this with absolute paths as needed for better security @@ -64,15 +103,6 @@ else export NOT_MAIL_HOST_P=t fi - -source /a/bin/log-quiet/logq-function - -# not used -# if [[ -s /a/opt/alacritty/extra/completions/alacritty.bash ]]; then -# source /a/opt/alacritty/extra/completions/alacritty.bash -# fi - - source /a/bin/ds/beet-data @@ -162,7 +192,7 @@ slemacs() { sle() { # sl emacs local f=/home/iank/.emacs.d/init.el - 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 "$@" + sl --sl-test-cmd "sed -rn '/^VERSION=/{s/^.*=//;s/[^[:alnum:]]//gp}' /etc/os-release; test -e $f && stat -c%Y $f" --sl-test-hook slemacs "$@" } ccomp ssh sle @@ -197,6 +227,20 @@ rm-docker-iptables() { # usage mkschroot [-] distro codename packages # - means no piping in of sources.list +# +# note some useful post mkschroot i've used in the past +# tu /nocow/schroot/flidas/etc/sudoers </etc/exim4/no-delay-eximids + echo all >/var/spool/exim4/gw/.no-delay-eximids + if [[ $EUID == 0 ]]; then + chown iank:iank /var/spool/exim4/gw/.no-delay-eximids + fi } edelayon() { - echo >/etc/exim4/no-delay-eximids + echo >/var/spool/exim4/gw/.no-delay-eximids + if [[ $EUID == 0 ]]; then + chown iank:iank /var/spool/exim4/gw/.no-delay-eximids + fi } eqgo() { local -a array tmpstr delayon delayon=true - if grep -qFx all /etc/exim4/no-delay-eximids; then + if grep -qFx all /var/spool/exim4/gw/.no-delay-eximids; then delayon=false fi if $delayon; then - echo all >/etc/exim4/no-delay-eximids + echo all >/var/spool/exim4/gw/.no-delay-eximids fi tmpstr=$(exiqgrep -i -r.\*) mapfile -t array <<<"$tmpstr" enn -M "${array[@]}" if $delayon; then - echo >/etc/exim4/no-delay-eximids + echo >/var/spool/exim4/gw/.no-delay-eximids fi } eqgo1() { local eid eid="$(exipick -i -r.\*|h1)" - sed -n "/^all$/p;\$a $eid" /etc/exim4/no-delay-eximids + sed -n "/^all$/p;\$a $eid" /var/spool/exim4/gw/.no-delay-eximids enn -M "$eid" } +# exim -M (in namespace and without delay) ennm() { local eid for eid; do - printf "%s\n" "$eid" >>/etc/exim4/no-delay-eximids + printf "%s\n" "$eid" >>/var/spool/exim4/gw/.no-delay-eximids done enn -M "$@" } @@ -368,11 +419,6 @@ cp-blocked-domains-to-ansible() { } -anki() { - # crashes on adding new cards in t9 - schroot -c buster -- anki -} - daycat() { ngset hrcat /m/md/daylert/{cur,new}/* @@ -422,7 +468,7 @@ ralerts() { # remote alerts ap() { # pushd in case current directory has an ansible.cfg file pushd /a/xans >/dev/null - ansible-playbook -v -l ${1:- $(hostname -f)} site.yml + ansible-playbook -v -i ${1:- $(hostname -f)}, site.yml popd >/dev/null } aw() { @@ -454,7 +500,7 @@ glue() { # usage: see above _iki-convert() { - local url url_prefix path input err repo_dir dir url_dir url name + local url url_prefix path input repo_dir dir url_dir url name url_prefix="$1" name="${url_prefix%%.*}" repo_dir="/f/$name" @@ -467,8 +513,16 @@ _iki-convert() { case $input in http*) path="$repo_dir/${input##http*://"$url_prefix"/}" + # for files like x.jpg, we dont need to convert the extension. if [[ $path == */ ]]; then path=${path%/}.mdwn + # brains adds trailing slash, but without trailing is still + # valid. We can't be totally sure whether to add mdwn, but we + # can guess based on the existence of the file. We can't be sure + # because it could be a file like x.jpg, that we just don't have + # in our local repo. + elif [[ ! -f $path && -e $path.mdwn ]]; then + path=${path}.mdwn fi j printf "%s\n" "$path" ;; @@ -476,7 +530,9 @@ _iki-convert() { path=$(fp "$input") url_dir=$(echo "$path" | sed -r "s,^(/a)?$repo_dir/,,") url="https://$url_prefix/$url_dir" - url="${url%.mdwn}/" + if [[ $url == *.mdwn ]]; then + url="${url%.mdwn}/" + fi j echo "$url" ;; esac @@ -729,6 +785,60 @@ mpvrpc-percent-pos() { mpvrpco '{ "command": ["get_property", "percent-pos"] }' | jq .data | sed 's/\..*/%/' 2>/dev/null ||: } +# run if not running. +# +# Note: this does not work with shell scripts as they are normally +# invoked, because the ps output has the interpreter at the start. +# A workaround is to invoke the command in that format, or we could +# do various other workarounds. +# +# background, this relies on how ps converts newlines in arguments to spaces, and +# assumes we won't be searching for a command with spaces in its arguments +rinr() { + # shellcheck disable=SC2009 # pgrep has no fixed string option, plus see above. + if ps h -o args -C "${1##*/}" | grep -Fxqv "$*" &>/dev/null || [[ $? == 141 ]]; then + "$@" + fi +} +# variation of above: run or wait if running +rowir() { + local pid + pid=$(ps h -o 'pid,args' -C "${1##*/}" | sed -r 's/^[[:space:]]*([0-9]+)[[:space:]](.*)/\1\n\2/' | grep -B1 -Fx "$*" | head -n1 ||: ) + if [[ $pid ]]; then + # https://unix.stackexchange.com/questions/427115/listen-for-exit-of-process-given-pid + tail --pid="$pid" -f /dev/null + else + "$@" + fi +} + +mpvrpc-loadfile() { + local path nextpath cachedir finalpath nextpath count + cachedir=$HOME/.iank-music-cache + path="$1" + nextpath="$2" + + # note: logic duplicated in beetpull + local remote_p=true + if [[ $HOSTNAME == kd ]]; then + remote_p=false + fi + + if $remote_p; then + finalpath="$cachedir${path#/i/m}" + rowir rsync --partial -a --inplace --mkpath "b8.nz:$path" "$finalpath" + finalnextpath="$cachedir${nextpath#/i/m}" + count=$(pgrep -a -f "^rsync --partial -a --inplace --mkpath $cachedir" || [[ $? == 1 ]] ) + # allow us to start 2 rsyncs in the background + if [[ $count == [01] ]]; then + rinr rsync --partial -a --inplace --mkpath "b8.nz:$nextpath" "$finalnextpath" & + fi + else + finalpath="$path" + fi + mpvrpc '{ "command": ["loadfile", "'"$finalpath"'"] }' +} + # tag with beets. # usage: beetag [-r] [-s] QUERY # it lists the query, reads an input char for tagging one by one. @@ -748,9 +858,10 @@ mpvrpc-percent-pos() { # q quit # ret next # +# todo: enter should also unpause beetag() { local last_genre_i fstring tag id char new_item char_i genre tag remove doplay i j random path - local do_rare_genres read_wait help line lsout tmp ls_line skip_lookback + local do_rare_genres read_wait line lsout tmp ls_line skip_lookback local escape_char escaped_input expected_input skip_input_regex right_pad erasable_line seek_sec local pl_state_path pl_state_dir pl_state_file tmpstr local new_random pl_seed_path seed_num seed_file fmt first_play repeat1 @@ -787,7 +898,8 @@ beetag() { fi ### end arg processing ### - beetpull + # note: I used to do beetpull here, but mpv + ssfs on slowish + # connection leads to bad/buggy result. do_rare_genres=false volume=70 @@ -814,7 +926,7 @@ beetag() { else pl_state_file=sorted fi - pl_state_dir=/i/info/pl-state + pl_state_dir=/b/data/pl-state if [[ $playlist ]]; then pl_state_dir=$pl_state_dir/$playlist else @@ -916,13 +1028,13 @@ beetag() { first_play=false for (( i=0; i<20; i++ )); do if [[ $(mpvrpco '{ "command": ["get_property", "idle-active"] }' 2>/dev/null | jq .data) == true ]]; then - mpvrpc '{ "command": ["loadfile", "'"$path"'"] }' 2>/dev/null + mpvrpc-loadfile "$path" 2>/dev/null break fi sleep .1 done else - mpvrpc '{ "command": ["loadfile", "'"$path"'"] }' + mpvrpc-loadfile "$path" fi erasable_line=false fi @@ -960,7 +1072,7 @@ beetag() { doplay=false else doplay=true - mpvrpc '{ "command": ["loadfile", "'"$path"'"] }' + mpvrpc-loadfile "$path" erasable_line=false fi beetag-nostatus 1 @@ -1419,7 +1531,6 @@ btrbk-host-debug() { # $ dig ns1.gnu.org @b0.org.afilias-nst.org. -# todo: make sm pull/push use systemd instead of the journal cat command bbk() { # btrbk wrapper local ret=0 c / @@ -1598,11 +1709,9 @@ jdo() { if [[ $cmd != /* ]]; then cmd=$(type -P "$cmd") fi + #note date format for since is date '+%F %T' # -q = quiet - journalctl -qn2 -f -u "$cmd_name" & - # Trial and error of time needed to avoid missing initial lines. - # .5 was not reliable. 1 was not reliable. 2 was not reliable - sleep 4 + journalctl --since=now -qn2 -f -u "$cmd_name" & jr_pid=$! # note, we could have a version that does system --user, but if for example # it does sudo ssh, that will leave a process around that we can't kill @@ -1667,7 +1776,13 @@ lipush() { local p a # excluding emacs for now #p=(/a/opt/{emacs-debian11{,-nox},mu,emacs} /a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts}) - p=(/a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts} /c/roles/prom_export/files/simple/usr/local/bin/fsf-install-node-exporter /a/opt/fpaste) + p=( + /a/bin /a/exe /a/h /a/c /p/c/machine_specific/vps{,.hosts} + /c/roles/prom_export/files/simple/usr/local/bin/fsf-install-node-exporter + /a/opt/fpaste + /p/c/user-specific/www-data/icecast-fsf{,-tech}-htpasswd + /p/c/icecast.xml + ) a="-ahviSAXPH --specials --devices --delete --relative --exclude-from=/p/c/li-rsync-excludes" ret=0 for h in li je bk; do @@ -1698,19 +1813,13 @@ bindpush() { dsign iankelling.org expertpathologyreview.com zroe.org amnimal.ninja lipush for h in li bk; do - m sl $h.b8.nz <<'EOF' -source ~/.bashrc -m dnsup -EOF + m ssh iank@$h.b8.nz dnsup done } bindpushb8() { lipush for h in li bk; do - m sl $h <<'EOF' -source ~/.bashrc -m dnsb8 -EOF + m ssh $h.b8.nz dnsb8 done } @@ -1721,8 +1830,18 @@ dnsup() { dnsb8() { local f=/var/lib/bind/db.b8.nz m ser stop named - m sleep 1 - m sudo rm -fv $f.jnl $f.signed.jnl + # jbk is like a temp file. dunno if removing it helps + + i=0 + while pgrep '^named$' &>/dev/null; do + sleep .5 + i=$(( i + 1 )) + if (( i > 100 )); then + echo "dnsb8: error: timeout waiting for named to exit" + return 1 + fi + done + m sudo rm -fv $f.jnl $f.signed.jnl $f.jbk m sudo install -m 644 -o bind -g bind /p/c/machine_specific/vps/bind-initial/db.b8.nz $f m ser restart named } @@ -1766,7 +1885,13 @@ dsign() { # set day start for use in other programs. # expected to do be in a format like 830, or 800 or 1300. ds() { + local regex + regex='[0-9]?[0-9]?[0-9][0-9]' if [[ $1 ]]; then + if [[ ! $1 =~ $regex ]]; then + echo "ds: error. expected \$1 to match $regex, got \$1: $1" + return 1 + fi echo $1 >/b/data/daystart else cat /b/data/daystart @@ -1808,64 +1933,16 @@ satoshi() { # $1 satoshi in usd } # Bitcoin holds open the wallet file. this causes problems for a -# secondary computer running bitcoin and receiving a backup. So, as a -# workaround, I intend to manually enable the wallet when I want to use -# it and leave it disabled otherwise. -walleton() { - local active - active=false - no_on=true - if [[ ! $(readlink -f /var/lib/bitcoind/wallets) == /q/wallets ]]; then - if systemctl --quiet is-active bitcoind; then - if [[ -e /tmp/no-bitcoinon ]]; then - no_on=true - else - if [[ $EUID == 0 ]]; then - m install -T -o iank -g iank /dev/null /tmp/no-bitcoinon - else - m touch /tmp/no-bitcoinon - fi - fi - active=true - m ser stop bitcoind - fi - m s ln -s /q/wallets /var/lib/bitcoind - sudo chown -h bitcoin:bitcoin /var/lib/bitcoind/wallets - if $active; then - m ser start bitcoind - if ! $no_on; then - m rm /tmp/no-bitcoinon - fi - fi - fi -} -walletoff() { - local active - active=false - no_on=true - if [[ $(readlink -f /var/lib/bitcoind/wallets) == /q/wallets ]]; then - if systemctl --quiet is-active bitcoind; then - if [[ -e /tmp/no-bitcoinon ]]; then - no_on=true - else - if [[ $EUID == 0 ]]; then - m install -T -o iank -g iank /dev/null /tmp/no-bitcoinon - else - m touch /tmp/no-bitcoinon - fi - fi - active=true - m ser stop bitcoind - fi - m rm /var/lib/bitcoind/wallets - if $active; then - m ser start bitcoind - if ! $no_on; then - m rm /tmp/no-bitcoinon - fi - fi - fi -} +# secondary computer running bitcoin and receiving a backup (as of +# 2023). However, in 2024-02, I ran a backup where a receiving machine +# had the wallet enabled and there was no error, so I don't know if this +# is still an issue or likely it is an inconsistent behavior. +# Note: a pruned node won't allow for a wallet to be added, super lame +# so i'm just not running a bitcoin node for now. +# Error: Prune: last wallet synchronisation goes beyond pruned data. You +# need to -reindex (download the whole blockchain again in case of +# pruned node) +# #### end bitcoin related things @@ -1891,12 +1968,81 @@ capache() fi } -chrome() { + + +apache-header() { + # First paragraph is to avoid people being confused about why a + # file is apache licensed. + cat <<'EOF' +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +EOF + +} + +# apply apache to git tracked bash files + README, except files with A?GPL3 header. +apache-apply-repo() { + for f in $(git ls-files); do + [[ -L $f || ! -f $f ]] && continue + if [[ $f != README ]]; then + if ! grep -n '^#!/bin/bash' $f | grep ^1: &>/dev/null; then continue; fi + if head -n 10 $f | grep 'it under the terms of the GNU General Public License as published by' &>/dev/null; then continue; fi + fi + apache-apply $f + done +} + +apache-apply() { + for file; do + if [[ ! -e $file ]]; then + echo '#!/bin/bash' >$file + chmod +x $file + fi + if head -n1 "$file"| grep -E '^#!/' &>/dev/null; then + { + head -n1 "$file" + apache-header + tail -n+2 "$file" + } | sponge "$file" + else + { + apache-header + cat "$file" + } | sponge "$file" + fi + done +} +# strip out the apache license from a file. +apache-strip() { + # shellcheck disable=SC2044 # meh + for f in $(find . -type f -maxdepth 1); do if head -n1 "$f"| grep -E '^#!/bin/bash\b' &>/dev/null; then { head -n 20 $f | tac | sed '/^# limitations under the License.$/,/^# Copyright.*Ian Kelling$/d' | tac; tail -n+21 $f; } |sponge $f; fi ; done +} + +chro() { if type -p chromium &>/dev/null; then cmd=chromium else cd / - cmd="schroot -c bullseye chromium" + cmd="schroot -c bookworm chromium" CHROMIUM_FLAGS='--enable-remote-extensions' $cmd & r fi } @@ -1939,10 +2085,6 @@ digme() { digdiff @ns{1,2}.iankelling.org "$@" } -tsr() { # ts run - "$@" |& ts || return $? -} - dup() { local ran_d ran_d=false @@ -1995,7 +2137,7 @@ fastboot() { kdecd() { /usr/lib/x86_64-linux-gnu/libexec/kdeconnectd; } -bat() { +batp() { cat /sys/class/power_supply/BAT0/capacity } @@ -2182,8 +2324,8 @@ rename-test() { # test whether missing files were renamed, generally for use with fsdiff # $1 = fsdiff output file, $2 = directory to compare to. pwd = fsdiff dir # echos non-renamed files - local x y found - unset sums + local x line found renamed + local -a sums for x in "$2"/*; do { sums+=( "$(md5sum < "$x")" ) ; } 2>/dev/null done @@ -2235,8 +2377,8 @@ gup() { /a/f/gnulib/build-aux/gnupload "$@"; } dejagnu() { /a/opt/dejagnu/dejagnu "$@"; } +# do git status on published repos. hstatus() { - # do git status on published repos. c /a/bin/githtml for x in *; do cd "$(readlink -f $x)"/.. @@ -2250,6 +2392,16 @@ hstatus() { done } +hsk() { + local x + c /a/bin/githtml + for x in *; do + cd "$(readlink -f $x)"/.. + skgit + cd /a/bin/githtml + done +} + ## work log # # note: database location is specified in ~/.timetrap.yml, currently /p/.timetrap.db @@ -2399,15 +2551,21 @@ ilog-local() { cd $d$n/"$chan" hr for x in *; do - echo $x; sed "s/^./${x%log}/" $x; hr; + # *** are parts and joins and such, and they make reading hard. + # I probably will want to see them sometimes, just have to + # remove that part. + echo $x; sed "s/^./${x%log}/;/\*\*\*/d" $x; hr; done done } ilog() { - local chan + local chan tmpf + tmpf=$(mktemp) chan="${1:-#fsfsys}" # use * instead of -r since that does sorted order - sl root@iankelling.org ilog-local "$chan" | less +G + sl root@li.b8.nz ilog-local "$chan" > $tmpf + less +G $tmpf + rm -f $tmpf } o() { @@ -2462,21 +2620,148 @@ wgkey() { umask $umask_orig } -declare -A vpn_ips -vpn_ips[kd]=2 -# note: 1, 4, 5 are occupied by mail wireguard -vpn_ips[x3]=8 -vpn_ips[sy]=12 -vpn_ips[x2]=13 -vpn_ips[kw]=27 -vpn_ips[bo]=28 -vpn_ips[frodo]=34 +host-info-all() { + host-info-update + + bindpushb8 + # for wireguard configs + ssh iank@li.b8.nz conflink + wrt-setup +} + + + + + +## for updating host info like ip, location, update /p/c/host-info and +## host_info below. the host_info array should probably be in its own +## file that gets sourced so that it can be more easily updated. + +# todo: this is so long that it becomes confusing, +# try to split it up. +# +# To make some changes take effect, run host-info-all. +host-info-update() { + + local -A vpn_ips host_ips host_macs portfw_ips nonvpn_ips all_ips + local -a root_hosts nonroot_hosts + + # the hosts with no mac + root_hosts=( bk je li b8.nz ) + for h in ${root_hosts[@]}; do + root_hosts+=(${h}ex) + done + root_hosts+=(cmc) + + while read -r ip host mac opts; do + if [[ $ip == *#* || ! $host ]]; then continue; fi + + # opt parsing + vpn=false + root=false + for opt in $opts; do + case $opt in + user=root) + root=true + ;; + vpn) + vpn=true + ;; + esac + done + + all_ips[$host]=$ip + if $vpn; then + portfw_ips[$host]=$ip + vpn_ips[$host]=$ip + else + nonvpn_ips[$host]=$ip + fi + if $root; then + # note: the reason we have b8.nz suffix here but not for non_root + # hosts is that it is for the User part, the IdentityFile part is + # redundant to *.b8.nz. Also note ${host}i, we only setup those for vpn hosts, but there is no harm in overspecifying here. + root_hosts+=($host ${host}i $host.b8.nz ${host}i.b8.nz) + # shellcheck disable=SC2004 # false positive + root_hosts_a[$host]=t # a for associative array + else + nonroot_hosts+=($host ${host}i) + fi + host_ips[$host]=$ip + if [[ $mac ]]; then + host_macs[$host]=$mac + fi + + done

/p/c/cmc-firewall-data + -vpn-ips-update() { local host ipsuf f files + + # shellcheck disable=SC2016 # shellcheck doesnt know this is sed + sedi '/edits below here are made automatically/,$d' /p/c/machine_specific/li/filesystem/etc/wireguard/wgmail.conf for host in ${!vpn_ips[@]}; do + if [[ ${root_hosts_a[$host]} ]]; then + # root machines dont actually need vpn, but + # the classification still helps with other + # configurations. + continue + fi ipsuf=${vpn_ips[$host]} wghole $host $ipsuf + u /b/ds/machine_specific/li/filesystem/etc/openvpn/client-config-hole/$host <$tmpf + cedit -e work-identity /p/c/subdir_files/.ssh/config-static <$tmpf + rm -f $tmpf + + ### begin focus on hosts file update ### + # + # This started as its own function, but it actually + # needed to alter the ssh config, so combined it. + # + # background: This is finally doing dynamic ip resolution via the hosts + # file. I considered detecting where each host was dynamically or + # something, but ultimately decided to mostly avoid that, other than + # detecting the status of the current machine I'm on. I want to be able + # to move it around without having to manually type much of anything. + local -a host_domain_suffix hosts + local -A ip_to_hosts + local suf ip i host at_home suf_from_here + + source /p/c/domain-info + + at_home=false + if ip n | grep -q "10.2.0.1 .* b4:75:0e:fd:06:4a"; then + at_home=true + fi + + for i in ${host_domain_suffix[@]}; do + if [[ $i == *.* ]]; then + suf=$i + continue + fi + hosts+=($i) + if [[ $i == "$HOSTNAME" ]]; then + unset "portfw_ips[$i]" + continue + fi + + suf_from_here=$suf + if ! $at_home && [[ $suf == .b8.nz || $suf == [wc].b8.nz ]]; then + suf_from_here=i.b8.nz + else + unset "portfw_ips[$i]" + fi + + # note this might be outdated until we do a dns push + ip=$(dig +short "$i$suf_from_here" @iankelling.org | tail -n1) ||: + if [[ ! $ip ]]; then + if [[ $suf == .office.fsf.org ]]; then + suf_from_here=wg.b8.nz + ip=$(getent ahostsv4 "$i$suf_from_here" | awk '{ print $1 }' | head -n1) ||: + fi + fi + if [[ $ip ]]; then + ip_to_hosts[$ip]+=" $i" + else + echo error: failed to get ip of "$i$suf_from_here" fi done + + for ip in "${!ip_to_hosts[@]}"; do + echo "$ip${ip_to_hosts[$ip]}" + done | s cedit -e hosts-file-up /etc/hosts + for host in ${hosts[@]}; do + echo $host + done >/p/c/subdir_files/.dsh/group/btrbk + ### end focus on hosts file update ### + + + # note: note sure if this is a great way to check. + # todo: think about it + + if $at_home; then + # possible that in the future we want to create + # a dynamic file here, and then we can move the cat + # command above out of the conditional + rsync -a /p/c/subdir_files/.ssh/config-static ~/.ssh/config + else + for host in ${!portfw_ips[@]}; do + ipsuf=${portfw_ips[$host]} + cat < ~/.ssh/config-dynamic + cat /p/c/subdir_files/.ssh/config-static ~/.ssh/config-dynamic >~/.ssh/config + fi } # usage host ipsuf [extrahost] @@ -2628,40 +3033,59 @@ EOF ) } +# sudo maybe +# +# passes on any initial -* args to sudo. +sudm() { + local arg + local -a sudo_opts + for arg; do + if [[ $arg == -* ]]; then + sudo_opts+=("$arg") + shift + else + break + fi + done + if [[ $EUID == 0 ]]; then + "$@" + else + sudo "${sudo_opts[@]}" "$@" + fi +} -mns() { # mount namespace +mns-setup() { + local ns ns=$1 - shift - s mkdir -p /root/mount_namespaces - if ! sudo mountpoint /root/mount_namespaces >/dev/null; then - m sudo mount --bind /root/mount_namespaces /root/mount_namespaces + sudm mkdir -p /root/mount_namespaces + if ! sudm mountpoint /root/mount_namespaces >/dev/null; then + m sudm mount --bind /root/mount_namespaces /root/mount_namespaces fi - m sudo mount --make-private /root/mount_namespaces - if [[ ! -e /root/mount_namespaces/$ns ]]; then - m sudo touch /root/mount_namespaces/$ns + m sudm mount --make-private /root/mount_namespaces + if ! sudm test -e /root/mount_namespaces/$ns; then + m sudm touch /root/mount_namespaces/$ns fi - if ! sudo mountpoint /root/mount_namespaces/$ns >/dev/null; then - m sudo unshare --propagation slave --mount=/root/mount_namespaces/$ns /bin/true + if ! sudm mountpoint /root/mount_namespaces/$ns >/dev/null; then + m sudm unshare --propagation slave --mount=/root/mount_namespaces/$ns /bin/true fi - m sudo -E /usr/bin/nsenter --mount=/root/mount_namespaces/$ns "$@" + +} + +mns() { # mount namespace + local ns + ns=$1 + shift + mns-setup $ns + m sudm -E /usr/bin/nsenter --mount=/root/mount_namespaces/$ns "$@" } mnsd() { # mount namespace + systemd namespace + local ns unit ns=$1 unit=$2 shift 2 - s mkdir -p /root/mount_namespaces - if ! sudo mountpoint /root/mount_namespaces >/dev/null; then - m sudo mount --bind /root/mount_namespaces /root/mount_namespaces - fi - m sudo mount --make-private /root/mount_namespaces - if [[ ! -e /root/mount_namespaces/$ns ]]; then - m sudo touch /root/mount_namespaces/$ns - fi - if ! sudo mountpoint /root/mount_namespaces/$ns >/dev/null; then - m sudo unshare --propagation slave --mount=/root/mount_namespaces/$ns /bin/true - fi + mns-setup $ns pid=$(servicepid $unit) tmpf=$(mktemp --tmpdir $unit.XXXXXXXXXX) @@ -2783,7 +3207,7 @@ mdenable() { two=false case $1 in - -2) two=true shift ;; + -2) two=true; shift ;; esac for md; do @@ -2864,6 +3288,14 @@ mpvgpu() { mpvd() { mpv --profile=d "$@"; } +mpva() { + mpv --profile=a "$@"; +} +# mpv for testing video quality, dont scale. +mpvt() { + mpv --video-unscaled "$@"; + } + # mpv all media files in . or $1 mpvm() { local -a extensions arg @@ -2962,8 +3394,8 @@ myprof() { pushd /home/iank/.local/share/profanity/chatlogs/iank_at_fsf.org/rooms/office_at_conference.fsf.org logs=(*) logcount=${#logs[@]} - if (( logcount > 15 )); then - i=$(( logcount - 15 )) + if (( logcount > 16 )); then + i=$(( logcount - 16 )) else i=0 fi @@ -3172,7 +3604,7 @@ j() { "$@" |& pee "xclip -r -selection clipboard" cat } -# x copy +# xorg copy. copy text piped into command xc() { xclip -r -selection clipboard } @@ -3334,25 +3766,31 @@ EOFOUTER chmod +x $out } -smeld() { # ssh meld usage host1 host2 file +# ssh meld. usage: host1 host2 file +smeld() { meld <(ssh $1 cat $3) <(ssh $2 cat $3) } +# remote file meld +# usage: host file1 file2 +rmeld() { + local tmpdir + tmpdir=$(mktemp -d) + scp "$1:$2" "$1:$3" $tmpdir + meld "$tmpdir/${2##*/}" "$tmpdir/${3##*/}" +} + + spd() { PATH=/usr/local/spdhackfix:$PATH command spd "$@" } spamf() { # spamtest on FILE - local spamcpre spamdpid - if (( $# != 1 )); then e spamtest error: expected 1 arg, filename >&2 return 1 fi - - spamdpid=$(systemctl status spamassassin| sed -n '/^ *Main PID:/s/[^0-9]//gp') - spamcpre="nsenter -t $spamdpid -n -m" - s $spamcpre sudo -u Debian-exim spamassassin -t --cf='score PYZOR_CHECK 0' <"$1" + sdncmdroot spamassassin sudo -u Debian-exim spamassassin -t --cf='score PYZOR_CHECK 0' <"$1" } @@ -3491,7 +3929,7 @@ torshell() { } eless2() { - less /var/log/exim4/mymain + less /var/log/exim4/nondmain } @@ -3596,16 +4034,16 @@ tu() { $s /a/exe/teeu "$@" } +# execute exim in its namespace. Useful args like -Mrm enn() { local ecmd pid - ecmd="/usr/sbin/exim4 -C /etc/exim4/my.conf" + ecmd="/usr/sbin/exim4 -C /etc/exim4/nn-mainlog.conf" if ip a show veth1-mail &>/dev/null; then s $ecmd "$@" - return + else + sdncmdroot exim4 $ecmd "$@" fi - pid=$(pgrep -f "/usr/sbin/exim4 -bd -q10m -C /etc/exim4/my.conf"|h1) - m s nsenter -t $pid -n -m $ecmd "$@" } # get pid of systemd service @@ -3647,7 +4085,7 @@ sdnbash() { # systemd namespace bash m sudo nsenter -t $pid -n -m sudo -u $USER -i bash } -sdnbashroot() { # systemd namespace bash +sdnbashroot() { # systemd namespace bash as root local unit pid if (( $# != 1 )); then echo $0: error wrong number of args >&2 @@ -3659,9 +4097,11 @@ sdnbashroot() { # systemd namespace bash } -sdncmd() { # systemd namespace cmd +# systemd namespace cmd +# usage: UNIT CMD... +sdncmd() { local unit pid tmpf - if (( $# <= 2 )); then + if (( $# <= 1 )); then echo $0: error wrong number of args >&2 return 1 fi @@ -3675,6 +4115,37 @@ sdncmd() { # systemd namespace cmd m sudo nsenter -t $pid -n -m sudo -u $USER -i bash -c ". $tmpf & rm $tmpf" } +sdncmdroot() { # systemd namespace root command + local unit pid + if (( $# < 2 )); then + echo $0: error wrong number of args >&2 + return 1 + fi + unit=$1 + shift + pid=$(servicepid $unit) + m sudo nsenter -t $pid -n -m "$@" +} + + +# systemd network namespace (not mount) cmd +# usage: UNIT CMD... +sdnncmd() { + local unit pid tmpf + if (( $# <= 1 )); then + echo $0: error wrong number of args >&2 + return 1 + fi + unit=$1 + shift + pid=$(servicepid $unit) + tmpf=$(mktemp --tmpdir $unit.XXXXXXXXXX) + export -p >$tmpf + printf "%s " "${@@Q}" >>$tmpf + echo >>$tmpf + m sudo nsenter -t $pid -n sudo -u $USER -i bash -c ". $tmpf & rm $tmpf" +} + mailnnbash() { sdnbash mailnn @@ -3686,13 +4157,7 @@ mailnnbash() { # } eximbash() { - local pid - pid=$(pgrep -f "/usr/sbin/exim4 -bd -q10m -C /etc/exim4/my.conf"|h1) - if [[ ! $pid ]]; then - echo "eximbash: failed to find exim pid. systemctl -n 30 status exim4:" - systemctl status exim4 - fi - m sudo nsenter -t $pid -n -m + sdnbashroot exim4 } spamnn() { local spamdpid @@ -3700,17 +4165,24 @@ spamnn() { m sudo nsenter -t $spamdpid -n -m sudo -u Debian-exim spamassassin "$@" } unboundbash() { - m sudo nsenter -t "$(systemctl status unbound| sed -n '/^ *Main PID:/s/[^0-9]//gp')" -n -m sudo -u $USER -i bash + sdnbashroot unbound } nmtc() { s nmtui-connect "$@" } +# check exim and others network namespace mailnncheck() { - local unit pid ns mailnn + local unit pid ns mailnn spamd_ser + + spamd_ser=spamd + if systemctl cat spamassassin &>/dev/null; then + spamd_ser=spamassassin + fi + # mailvpn would belong on the list if using openvpn - for unit in mailnn unbound dovecot spamassassin exim4 radicale; do + for unit in mailnn unbound dovecot $spamd_ser exim4 radicale; do pid=$(servicepid $unit) echo debug: unit=$unit pid=$pid if [[ ! $pid ]]; then @@ -3734,14 +4206,13 @@ mailnncheck() { vpncmd() { - m sudo -E env "PATH=$PATH" nsenter -t "$(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/.*client.conf")" -n "$@" + sdncmd openvpn-client-tr@client.service "$@" } - vpni() { - vpncmd sudo -u iank env "PATH=$PATH" "$@" + sdncmd openvpn-client-tr@client.service bash } vpnbash() { - vpncmd bash + sdncmdroot openvpn-client-tr@client.service bash } @@ -3753,10 +4224,7 @@ vpn() { fi [[ $1 ]] || { echo need arg; return 1; } - journalctl --unit=$vpn_service@$1 -f -n0 & - # sometimes the journal doesnt open until after the vpn output - # has happened. hoping this fixes that. - sleep 1 + journalctl --since=now --unit=$vpn_service@$1 -f -n0 & sudo systemctl start $vpn_service@$1 # sometimes the ask-password agent does not work and needs a delay. sleep .5 @@ -3775,21 +4243,21 @@ fixu() { fi } -# unmute +# unmute desktop output um() { - local sink card + local sink card sedcmd sink=$(pactl get-default-sink) - if [[ $sink != auto_null ]]; then - return + if [[ $sink == auto_null ]]; then + # guessing there is just one with an off profile. otherwise we will + # need some other solution, like storing the card identifier that we + # muted with nap. Or, we could so some hakery with + # pactl -f json. + sedcmd='/^[[:space:]]*index:/{s/^[[:space:]]*index://;h};/^[[:space:]]*active profile: $/{g;p;q}' + card=$(pacmd list-cards | sed -n "$sedcmd") + m pacmd set-card-profile "$card" output:analog-stereo fi - # guessing there is just one with an off profile. otherwise we will - # need some other solution, like storing the card identifier that we - # muted with nap. - card=$(pacmd list-cards | sed -n '/^[[:space:]]*index:/{s/^[[:space:]]*index://;h};/^[[:space:]]*active profile: $/{g;p;q}') - m pacmd set-card-profile "$card" output:analog-stereo - - pactl set-sink-mute @DEFAULT_SINK@ false + m pactl set-sink-mute @DEFAULT_SINK@ false rm -f /tmp/ianknap } @@ -3946,44 +4414,87 @@ vrun() { "$@" } -f=/a/f/ansible-configs/files/common/etc/fsf-workstation-bashrc.sh -if [[ -e $f ]]; then - # shellcheck disable=SC1090 - source $f -fi - electrum() { + # Running the appimage said fuse was not available, but try + # running the appimage with --appimage-extract, which worked. + # It seems there is no need to backup the wallet, it can be restored + # via the seed onto any computer that needs it. + /a/opt/electrum/squashfs-root/AppRun "$@" + + + # This was an old way I ran electrum over tor, and seems like I + # imported a bitcoin core wallet. + # # https://electrum.readthedocs.io/en/latest/tor.html # https://github.com/spesmilo/electrum-docs/issues/129 - s rsync -ptog --chown bitcoin:bitcoin ~/.Xauthority /var/lib/bitcoind/.Xauthority - sudo -u bitcoin DISPLAY=$DISPLAY XAUTHORITY=/var/lib/bitcoind/.Xauthority /a/opt/electrum-4.2.1-x86_64.AppImage -p socks5:localhost:9050 + # s rsync -ptog --chown bitcoin:bitcoin ~/.Xauthority /var/lib/bitcoind/.Xauthority + # sudo -u bitcoin DISPLAY=$DISPLAY XAUTHORITY=/var/lib/bitcoind/.Xauthority /a/opt/electrum-4.2.1-x86_64.AppImage -p socks5:localhost:9050 + } + + monero() { sudo -u bitcoin DISPLAY=$DISPLAY XAUTHORITY=/var/lib/bitcoind/.Xauthority /a/opt/monero-gui-v0.17.3.2/monero-wallet-gui } +# grep + find +gef() { + faf | grep -E "$@" ||: + rgv "$@" +} + # rg my main files rgm() { rg "$@" /p/w.org /a/t.org /a/work.org /b } -# re all my files more expansively +# re all my files more expansively. +# usage [-OPT...] regex space combined rem() { local paths + local -a opts + for arg; do + if [[ $arg == -* ]]; then + opts+=("$1") + shift + else + break + fi + done paths="/p/c /b/" find $paths -not \( -name .svn -prune -o -name .git -prune \ -o -name .hg -prune -o -name .editor-backups -prune \ -o -name .undo-tree-history -prune \) 2>/dev/null | grep -iP --color=auto -- "$*" ||: - rgv -- "$*" $paths /a/t.org /p/w.org /a/work.org ||: + rgv $local_rgv_args -g "!bash_unpublished" "${opts[@]}" -- "$*" $paths /a/work.org ||: +} +reml() { # rem with limit to 5 matches per file + local_rgv_args="-m 5" + rem "$@" +} + +rep() { + local paths + paths="/p/c" + find $paths -not \( -name .svn -prune -o -name .git -prune \ + -o -name .hg -prune -o -name .editor-backups -prune \ + -o -name .undo-tree-history -prune \) 2>/dev/null | grep -iP --color=auto -- "$*" ||: + rgv $local_rgv_args -- "$*" $paths /a/t.org /p/w.org ||: +} +repl() { # rem with limit to 5 matches per file + local local_rgv_args="-m 5" + rem "$@" } -reml() { # with limit to 5 matches per file + + +# re on common fsf files +ref() { local paths - paths="/p/c /b" + paths="/f/gluestick /f/brains /f/s /c" find $paths -not \( -name .svn -prune -o -name .git -prune \ -o -name .hg -prune -o -name .editor-backups -prune \ -o -name .undo-tree-history -prune \) 2>/dev/null | grep -iP --color=auto -- "$*" ||: - rgv -m 5 -- "$*" $paths /a/t.org /p/w.org /a/work.org ||: + rgv -- "$*" $paths /a/work.org ||: } @@ -4122,9 +4633,6 @@ mypyenvinit () { } -export GOPATH=$HOME/go -path-add $GOPATH/bin -path-add /usr/local/go/bin # I have the git repo and a release. either one should work. # I have both because I was trying to solve an issue that @@ -4146,7 +4654,15 @@ if type -P rg &>/dev/null; then # --no-messages because of annoying errors on broken symlinks # -z = search .gz etc files # -. = search dotfiles - 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 $?; } + # -n --no-heading: show files and line numbers together allowing for clicking + rg() { + local path_arg + if [[ ${#@} == 1 ]]; then + path_arg=. + fi + + command rg -. -z --no-messages -Lin --no-heading -M 900 --no-ignore-parent --no-ignore-vcs -g '!.git' -g '!auto-save-list' -g '!.savehist' "$@" $path_arg || return $? + } #fails if not exist. ignore complete -r rg 2>/dev/null ||: else @@ -4155,7 +4671,10 @@ fi # rg with respecting vcs ignore files rgv() { - ret=0 + local path_arg ret=0 + if [[ ${#@} == 1 ]]; then + path_arg=. + fi # settings that are turned off for pipes, keep them on. # Found by searching for "terminal" in --help # --heading @@ -4165,9 +4684,10 @@ rgv() { # -z = search zipped files # -i = case insensitive # -M = max columns + # -n --no-heading: show files and line numbers together allowing for clicking # --no-messages because of annoying errors on broken symlinks # --no-ignore-parent because i have /a/.git which ignores almost everything under it. - command rg -n --heading -. -z --no-messages -i -M 900 --no-ignore-parent -g '!.git' -g '!auto-save-list' -g '!.savehist' "$@" || ret=$? + command rg -n --no-heading -. -z --no-messages -i -M 900 --no-ignore-parent -g '!.git' -g '!auto-save-list' -g '!.savehist' "$@" $path_arg || ret=$? return $ret } @@ -4234,7 +4754,7 @@ hssh-update() { case $HOSTNAME in sy|kd) hosts=( - kd x3.office.fsf.org syw + kd.b8.nz x3.office.fsf.org syw x2.b8.nz ) ;; x3) @@ -4262,6 +4782,219 @@ i3bar() { rm -fv /tmp/noi3bar } +# example: +# <#part type="image/jpeg" filename="/home/iank/2023-12-24-ski-trip.jpg" disposition=attachment> <#/part> +# +attach-txt() { + local f + for f; do + if [[ ! -s $f ]]; then + e "error: empty or non-existent file $f" + return 1 + fi + done + for f; do + echo '<#part type="image/jpeg" filename="'"$(rl "$f")"'" disposition=attachment> <#/part>' + done | ec +} + +ctof() { + units "tempC($1)" tempF +} + +ftoc() { + units "tempF($1)" tempC +} + +# local icecast +localic() { + local mod=false + cedit live /p/c/machine_specific/vps/filesystem/var/lib/bind/db.iankelling.org <<'EOF' || mod=true +live CNAME b8.nz. +EOF + if $mod; then + ip=$(ip r show default | sed -r 's/.*src ([^ ]*).*/\1/' | head -n1) + if [[ ! $ip ]] && timeout 1 ping -c 1 $ip; then + echo "error: failed to get ip: $ip" >&2 + exit 1 + fi + cat >/p/c/cmc-firewall-data-http < +AuthType Basic +AuthName "basic_auth" +# created with +# htpasswd -c icecast-fsf-htpasswd USERNAME +AuthUserFile "/etc/icecast-fsf-htpasswd" +Require valid-user + + +AuthType Basic +AuthName "basic_auth" +AuthUserFile "/etc/icecast-fsf-tech-htpasswd" +Require valid-user + +EOF + s cat /etc/letsencrypt/live/live.iankelling.org/{fullchain,privkey}.pem | s dd of=/etc/icecast2/fullchainpluskey.pem + ser start icecast2 +} +# li icecast +liic() { + cedit live /p/c/machine_specific/vps/filesystem/var/lib/bind/db.iankelling.org <<'EOF' || bindpush +live A 72.14.176.105 + AAAA 2600:3c00::f03c:91ff:fe6d:baf8 +EOF +} +# icecast rm -r +icrmr() { + find /var/icecast -type f -delete + ssh li.b8.nz find /var/icecast -type f -delete +} + + +# obs screen switching of +obof() { + ls -l /tmp/no-obs-auto-scene-switch + touch /tmp/no-obs-auto-scene-switch +} +# obs screen switching on +obon() { + ls -l /tmp/no-obs-auto-scene-switch + if [[ -e /tmp/no-obs-auto-scene-switch ]]; then + rm -f /tmp/no-obs-auto-scene-switch + fi +} + +obs-gen-profiles() { + local p=/p/c/basic/profiles + sed 's/fsf-sysops/fsf-tech/g' $p/fsfsysops/basic.ini >$p/fsftech/basic.ini + sed 's/fsf-sysops/fsf/g' $p/fsfsysops/basic.ini >$p/fsf/basic.ini +} + +# terminal clear. like clear, but put the prompt at the bottom, +# useful for obs streaming the bottom half of a terminal window. +tclear() { + for ((i=0; i&1) || ret=1 + printf "%s\n" "$out" + if [[ $out == *"try diffing"* ]]; then + tmpdir=$(mktemp -d) + m scp cmc:/tmp/dhcp.leases cmc:/tmp/dhcp.leases.iank $tmpdir + m diff $tmpdir/dhcp.leases $tmpdir/dhcp.leases.iank ||: + rm -rf $tmpdir + fi + return $ret +} + +# ffs and switch the bash history on this terminal. +# disabled because I don't really need this and +# the history switching is annoying for debugging. +# +# ffs() { +# local last +# last="${*: -1}" +# if [[ $last && $last != -* && $last != sysops ]]; then +# his +# fi +# command ffs "$@" +# } + +i3gen() { + /b/ds/i3-sway/gen +} + + +# insensitive find plus edit +ife() { + local tmps found_count i char file + local -a found_files + local -A button_file + tmps=$(ifn "$@") + mapfile -t found_files <<<"$tmps" + found_count=${#found_files[@]} + if (( ${#found_files[@]} == 1 )); then + m g ${found_files[0]} + else + i=0 + for button in {a..z}; do + button_file[$button]="${found_files[$i]}" + echo $button: ${found_files[$i]} + i=$(( i + 1 )) + if (( i >= found_count )); then + break + fi + done + read -rsN1 -t 5 char ||: + file="${button_file[$char]}" + + if [[ $file ]]; then + g "$file" + else + echo "no selection" + fi + fi +} + +# decrease filesize without losing any noticeable quality. inspired from +# https://gist.github.com/BlueSwordM/86dfcb6ab38a93a524472a0cbe4c4100 +ffsencode() { + in="$1" + out="$2" + ffmpeg -i "$in" -c:v libsvtav1 -crf 60 -preset 6 -g 60 -svtav1-params tune=0:enable-overlays=1:scd=1:scm=1 -pix_fmt yuv420p10le -c:a copy "$out" +} export BASEFILE_DIR=/a/bin/fai-basefiles