├── run_via_cgroup
├── xsession-errors
├── avolume
├── zip2date
├── gamma.sh
├── cr
├── macros
├── rescan_scsi
├── any2wav
├── BTBL
├── findexe
├── check_installed_rpms_clean
├── nvidia-xfce-genmon
├── perfbias.sh
├── png2jxl
├── git-pusher
├── btc2usd
├── boost-tuner
├── vbox_system_qt
├── iperf3.service
├── git-file-dates
├── par2create-batch
├── watts_cpu
├── dmesg-notify
├── ip-country
├── firmware-updater.sh
├── monitor_overclock
├── check_installed_rpms
├── uniunpack
├── defrag
├── avoid-vpn
├── dmesg-notify-user
├── metaclone
├── direct
├── mouse-acceleration-toggle
├── audio-conf
├── tar_sorted
├── message_gui_user
├── ryzen-powersave.sh
├── clean_dnf
├── alert-bad-processes-user
├── alert-bad-processes
├── watch_raw_io
├── README.md
├── Firefox-updater
├── oneliners
├── cpumode
├── watchrawio.py
└── LICENSE
/run_via_cgroup:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | systemd-run --user --scope "$@"
3 |
--------------------------------------------------------------------------------
/xsession-errors:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # This works in modern Linux distros and under Wayland as well
4 | journalctl _UID=`id --user`
5 |
--------------------------------------------------------------------------------
/avolume:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # 2021-04-27 13:44:02
4 | amount=3
5 | test -n "$3" && amount="$3"
6 |
7 | volume=`audtool get-volume` || exit 1
8 |
9 | if [ "$1" = "+" ]; then
10 | audtool set-volume $((volume+amount))
11 | else
12 | audtool set-volume $((volume-amount))
13 | fi
14 |
--------------------------------------------------------------------------------
/zip2date:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # apply zip contents time to the zip files itself
4 | # Sat Dec 30 20:29:31 2017
5 |
6 | for i in "$@"; do
7 | if [ -n "`file "$i" 2>&1 | grep 'Zip archive data'`" ]; then
8 | nd=`unzip -l "$i" | awk '/[0-9][0-9]-..-..../{print $2" "$3}' | sort -n | tail -1 | sed 's/\(..\)-\(..\)-\(....\) \(..\):\(..\)/\3\1\2\4\5/'`
9 | echo "File '$i', date $nd"
10 | touch -t "$nd" "$i"
11 | else
12 | echo "Skipping file '$i': not a zip file"
13 | fi
14 | done
15 |
--------------------------------------------------------------------------------
/gamma.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | current=`xgamma 2>&1 | awk '{print $7}'`
4 |
5 | change()
6 | {
7 | new=1
8 | if [ "$1" = "+" -o "$1" = "-" ]; then
9 | new=`echo "${current}${1}0.05" | bc -l`
10 | fi
11 | xgamma -gamma "$new" 2>/dev/null
12 | xmessage -timeout 1 "New gamma value : $new" 2>/dev/null &
13 | }
14 |
15 | case $1 in
16 | "+" | "-" | "0" )
17 | change $1
18 | ;;
19 | * )
20 | echo "`basename $0` +/-/0"
21 | ;;
22 | esac
23 |
--------------------------------------------------------------------------------
/cr:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # Too tired of remembering all the flags required to launch Google Chrome
4 | # 2018-03-06 22:47:33
5 |
6 | # 2021-04-17 17:32:06 a fix for 27" display
7 |
8 | RES=`xdpyinfo | awk '/dimensions/{print $2}'`
9 | extra=
10 |
11 | if [ "$RES" == "2560x1440" ]; then
12 | extra=--force-device-scale-factor=1
13 | echo "Setting $extra"
14 | fi
15 |
16 | env TZ=USA/Pacific FREETYPE_PROPERTIES=truetype:interpreter-version=35 chrome --disk-cache-dir=/tmp/.chrome-cache $extra "$@"
17 |
--------------------------------------------------------------------------------
/macros:
--------------------------------------------------------------------------------
1 | # 2021-05-17 17:07:50
2 | # this is for RPM/dnf updates - don't update installed files which match the appropriate files from new packages
3 | %_minimize_writes 1
4 |
5 | # 2021-12-14 15:47:01 https://bugzilla.redhat.com/show_bug.cgi?id=2032066
6 | # Locales to install
7 | %_install_langs C:en:en_US:en_US.UTF-8:ru_RU:ru_RU.UTF-8
8 |
9 | # 2022-05-08 14:28:22
10 | # Do not install /usr/share/doc documents
11 | # 2022-05-10 22:05:13 - this also prevents man pages from being installed :(
12 | # Instead use dnf install [packages] --setopt=install_weak_deps=False --setopt='tsflags=nodocs'
13 | # %_excludedocs 1
14 |
--------------------------------------------------------------------------------
/rescan_scsi:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: force rescan all SCSI/SATA endpoints
4 | #
5 | # Author: Artem S. Tashkinov <>
6 | # Created at: 2007-12-17 14:03:45
7 | #
8 | # Copyright (c) 2007 Artem S. Tashkinov. All rights reserved.
9 | #----------------------------------------------------------------------
10 |
11 | SCSI=/sys/class/scsi_host
12 | test ! -d "$SCSI" && echo "Error: cannot find the $SCSI directory." && exit 1
13 | cd "$SCSI" || exit 1
14 |
15 | for i in *; do
16 | echo -n "Scanning $i ..."
17 | echo "- - -" > $i/scan && echo " done."
18 | done
19 |
20 | echo "Finished. Consult with 'dmesg' for details."
21 |
--------------------------------------------------------------------------------
/any2wav:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # Tue Jan 7 17:57:16 2014
4 | # Convert any media file with an audio stream to a wav file
5 |
6 | # Mon 04 Nov 2019 10:09:46 PM
7 | # mplayer -> ffmpeg; replace file extension instead of appending it - requires bash
8 |
9 | for i in "$@"; do
10 | echo -n "Processing '$i' ... "
11 | test -n "`echo "$i" | grep '\.wav$'`" && echo "$i is already a wav file. Skipping" && continue
12 | ext="${i##*.}"
13 | wav="${i%.$ext}.wav"
14 | test "$wav" == "$i" && wav="$i.wav" # In case I screwed up
15 | test -f "$wav" && echo "$wav already exists. Skipping." && continue
16 | ffmpeg -hide_banner -nostats -loglevel warning -i "$i" -map_metadata -1 -fflags +bitexact -vn "$wav" && echo "OK"
17 | done
18 |
--------------------------------------------------------------------------------
/BTBL:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # bluetooth battery level
4 | # 2022-06-28 01:27:46 - fixes
5 | # 2022-08-01 23:12:38 - show emoji
6 |
7 | dev=headset_dev_XX_XX_XX_XX_XX_XX # find it via upower -d
8 | test -n "$1" && dev="$1"
9 | getdata=`upower --show-info "/org/freedesktop/UPower/devices/$dev"`
10 | test -n "`echo "$getdata" | grep '(null)'`" && echo "⊝" && exit
11 | level=`echo "$getdata" | awk '/percentage/{print $2}' | sed 's/%//'`
12 |
13 | # check if it's a valid number
14 | case "$level" in
15 | ''|*[!0-9]*) exit 1;;
16 | esac
17 |
18 | # only show emoji for a graphical session
19 | if [ "$TERM" = "dumb" ]; then
20 | if [ "$level" -gt 39 ]; then # adjust to your situation: below 40% is already too low/little for me
21 | echo -n " 🔋"
22 | else
23 | echo -n " 🪫"
24 | fi
25 | fi
26 |
27 | echo "${level}%"
28 |
--------------------------------------------------------------------------------
/findexe:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: find executables by name located in in the PATH
4 | # environment variable
5 | #
6 | # Author: Artem S. Tashkinov <>
7 | # Created at: Wed Jun 13 02:53:14 2001
8 | # 2001-11-03 22:22:37 Changes unknown
9 | # Sat Mar 3 18:09:56 2018 Fix grep
10 | # Tue Jan 28 18:34:45 2020 rewritten to be safe
11 | #
12 | # Copyright (c) 2004-2020 Artem S. Tashkinov. All rights reserved.
13 | #----------------------------------------------------------------------
14 |
15 | test -z "$1" && echo "Usage is `basename "$0"` name" && exit 1
16 |
17 | echo "$PATH" | awk -F : '{ for (i=1;i<=NF;i++) print $i }' | while read -r cdir; do
18 | ls "$cdir" | grep -- "$1"
19 | done | while read -r name; do
20 | /usr/bin/which "$name"
21 | done | sort
22 |
--------------------------------------------------------------------------------
/check_installed_rpms_clean:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # 2019-02-23 15:59:17 - clean version
4 | # 2023-05-03 12:48:05 - egrep -> grep -E
5 |
6 | echo "Running ... "
7 |
8 | [ -n "$1" ] && nodeps="|Unsatisfied dependencies|is needed by"
9 |
10 | logf=/tmp/RPM-V.$RANDOM$RANDOM
11 | > $logf
12 |
13 | r[0]='-'
14 | r[1]='\'
15 | r[2]='|'
16 | r[3]='/'
17 | r[4]='-'
18 | r[5]='\'
19 | r[6]='|'
20 | r[7]='/'
21 |
22 | j=0
23 | n=1
24 |
25 | total=`rpm -qa | wc -l`
26 |
27 | for i in `rpm -qa | sort`; do
28 |
29 | p=$((n*100/total))
30 | echo -en "\r${r[$j]} $n [$p%]"
31 | RES=`nice -20 rpm -V "$i" | grep -E -v "ZZZXXXCCC$nodeps"`
32 |
33 | if [ -n "$RES" ]; then
34 | echo " ******************** $i ******************** " >> $logf
35 | echo "$RES" >> $logf
36 | fi
37 |
38 | n=$((n+1))
39 | j=$((j+1))
40 | test $j -gt 7 && j=0
41 |
42 | done
43 |
44 | echo
45 | echo "Done. $n packages verified. See log here: $logf"
46 |
--------------------------------------------------------------------------------
/nvidia-xfce-genmon:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # 2020-08-21 08:12:07 didn't sleep until 10am
4 | # 2020-08-22 03:34:07 replaced ρ with 🗘
5 |
6 | read temp draw fan driver pciev pciew loadgpu loadfb ramused pstate clocksgpu clocksmem plim < <(nvidia-smi \
7 | --query-gpu=temperature.gpu,power.draw,fan.speed,driver_version,pcie.link.gen.current,pcie.link.width.current,utilization.gpu,utilization.memory,memory.used,pstate,clocks.gr,clocks.mem,power.limit \
8 | --format=csv,noheader,nounits | sed 's/,//g')
9 |
10 | echo "${temp}° $(printf "%.1f" "$draw" 2>/dev/null)W 🗘 ${fan}"
11 | echo -e "GPU load: $loadgpu%\nGPU: $clocksgpu MHz\nFB load: $loadfb%\nGDDR: $clocksmem MHz\nVRAM: $ramused MiB\nPerf: $pstate\nPCI-E: v${pciev} x ${pciew}\nMax: $plim W\nDriver: $driver"
12 | echo "nvidia-settings"
13 |
--------------------------------------------------------------------------------
/perfbias.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # 2024-03-20 20:12:41 - first release
4 | # 2024-04-28 23:21:29 - simplify and improve, rewrite in pure bash
5 |
6 | # "default" is specified but doesn't work, wtf?
7 | modes='performance|balance_performance|balance_power|power'
8 |
9 | cpus=`ls /sys/devices/system/cpu/cpufreq/*/energy_performance_preference`
10 | test -z "$cpus" && echo "WTF?" && exit 1
11 |
12 | getter()
13 | {
14 | cat $cpus | sort -u
15 | }
16 |
17 | setter()
18 | {
19 | echo -n "Switching to: "
20 | echo "$1" | tee $cpus
21 | echo "Result: `getter`"
22 | }
23 |
24 | now=`getter`
25 | echo "Now: $now"
26 |
27 | if [[ "$1" =~ $modes && "$now" != "$1" ]]; then
28 | setter "$1"
29 | elif [ "$1" = "switch" ]; then
30 | if [[ "$now" =~ "power" ]]; then
31 | setter performance
32 | else
33 | setter power
34 | fi
35 | elif [ -z "$1" ]; then
36 | echo "Use is: `basename $0` [$modes|switch]"
37 | else
38 | echo "There's nothing to do"
39 | fi
40 |
--------------------------------------------------------------------------------
/png2jxl:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: convert all PNG files in the current directory
4 | # to JXL/JPEG XL while preserving timestamps
5 | #
6 | # Author: Artem S. Tashkinov
7 | # Created at: Wed Apr 16 13:26:42 2025
8 | # Computer: zen
9 | # System: Linux 6.13.8-zen3 on x86_64
10 | #
11 | # Copyright (c) 2025 Artem S. Tashkinov All rights reserved.
12 | #
13 | #----------------------------------------------------------------------
14 |
15 | #cjxl only supports PNG
16 | #for i in *.bmp *.png *.webp *.gif; do
17 | # output="${i%.*}.jxl"
18 | # operation "$i" "$output"
19 | #done
20 |
21 | # Let's speed it up and nice it to infinity
22 | encode() {
23 | input="$1"
24 | output="${input%.*}.jxl"
25 | nice -20 cjxl -q 100 --allow_expert_options -e 11 -v -v "$input" "$output" && touch -r "$input" "$output"
26 | }
27 |
28 | export -f encode
29 | parallel --keep-order --jobs 4 --halt now,fail=1 --color-failed --bar encode ::: *.png
30 |
--------------------------------------------------------------------------------
/git-pusher:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # A quickly hacked script to push files to GIT/github while preserving their original modification dates as git commits' dates
4 | # No licence. If you're gonna steal it, please retain this header, thanks!
5 | # Set debug to echo if you don't want to screw up everything
6 | # © 2024-05-21 12:58:00 UTC Artem S. Tashkinov
7 | #
8 | # Instructions
9 | # git init
10 | # git remote add origin git@github.com:user/repo.git
11 | #
12 | # Use it this way for all files at once:
13 | # find . -type f -not -path "*git*" -print0 | xargs -0 git-pusher
14 | #
15 | # or for individual files
16 | # git-pusher path/file
17 |
18 | set -x
19 | debug=echo
20 |
21 | for i in "$@"; do
22 | name=`echo "$i" | sed 's/\.\///'` # strip ./ from `find` output
23 | mtime=$(stat -c "%y" "$i")
24 | export GIT_AUTHOR_DATE="$mtime"
25 | export GIT_COMMITTER_DATE="$mtime"
26 | $debug git add "$name"
27 | $debug git commit -am "$name" # this sucks
28 | $debug git push --set-upstream origin master
29 | done
30 |
--------------------------------------------------------------------------------
/btc2usd:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # 2020-10-19 19:29:08
4 | # 2025-01-16 20:45:26 reordered, a hint for the XFCE command output applet
5 | # 2025-04-28 14:32:28 coindesk can go fuck themselves
6 |
7 | if jq --version &>/dev/null; then
8 |
9 | price1=`curl --silent --max-time 7 'https://data-api.cryptocompare.com/asset/v1/top/list?page=1&page_size=15&sort_by=CIRCULATING_MKT_CAP_USD&sort_direction=DESC&groups=ID%2CBASIC%2CSUPPLY%2CPRICE%2CMKT_CAP%2CVOLUME%2CCHANGE%2CTOPLIST_RANK&toplist_quote_asset=USD&response_format=JSON' | jq '.Data.LIST[] | select(.SYMBOL == "BTC") | .PRICE_USD'`
10 | price3=`curl --silent --max-time 7 'https://api.coingecko.com/api/v3/coins/bitcoin?developer_data=false&community_data=false&tickers=false' | jq '.market_data.current_price.usd'`
11 |
12 | printf " %'.f %'.f " "$price3" "$price1" 2>/dev/null
13 | echo "1st: coingecko.com; 2nd: coindesk.com via cryptocompare;"
14 | echo "firefox https://coingecko.com"
15 |
16 | else
17 |
18 | echo "Install jq"
19 |
20 | fi
21 |
--------------------------------------------------------------------------------
/boost-tuner:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: enable CPU boost only when given applications are running
5 | # will be unconditionally enabled if /tmp/boost exists
6 | #
7 | # Author: Artem S. Tashkinov
8 | # Created at: Sun 05 Jan 2020 06:57:51 PM
9 | # Computer: localhost.localdomain
10 | # System: Linux 5.4.2-zen2 on x86_64
11 | #
12 | # Copyright (c) 2020 Artem S. Tashkinov All rights reserved.
13 | #----------------------------------------------------------------------
14 |
15 | interval=10
16 | apps="^gcc|^cpp|^ld|^make|^configure|^cmake"
17 | fileon=/tmp/boost
18 |
19 | handler=/sys/devices/system/cpu/cpufreq/boost
20 |
21 | while :; do
22 |
23 | test ! -f "$handler" && echo "CPU frequency subsystem failure: boost not found" && exit 1
24 | result=`ps axco command | egrep "$apps"`
25 |
26 | if [ -n "$result" -o -f "$fileon" ]; then
27 | echo 1 > "$handler"
28 | else
29 | echo 0 > "$handler"
30 | fi
31 |
32 | sleep $interval
33 | done
34 |
--------------------------------------------------------------------------------
/vbox_system_qt:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: replace VirtualBox own Qt libraries with system ones
4 | # Author: Artem S. Tashkinov
5 | # Created at: Thu Apr 16 03:44:38 2020
6 | # Computer: zen
7 | # System: Linux 5.6.3-az2 on x86_64
8 | #
9 | # Copyright (c) 2020 Artem S. Tashkinov All rights reserved.
10 | #
11 | #----------------------------------------------------------------------
12 |
13 | test "$UID" -ne "0" && echo "Must be run under root" && exit 0
14 |
15 | cd /usr/lib/virtualbox || exit 1
16 |
17 | mkdir -p qt || exit 2
18 | for i in libQt*; do
19 | echo -n "Processing $i ... "
20 | test -L "$i" && echo "already processed" && continue
21 | mv -i "$i" qt || exit 3
22 | base=`echo "$i" | sed 's/VBox//'`
23 | ln -s "/usr/lib64/$base" "/usr/lib/virtualbox/$i" || exit 4
24 | echo " OK"
25 | done
26 |
27 | test -f qt.conf && mv -i qt.conf qt
28 | test -d plugins && mv -i plugins qt
29 |
30 | echo
31 | echo "Finished"
32 | echo "The original VirtualBox Qt libraries are at /usr/lib/virtualbox/qt"
33 |
--------------------------------------------------------------------------------
/iperf3.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=iperf3 server for network throughput measurement that uses port 5201 by default
3 |
4 | After=basic.target network.target network-online.target
5 | Before=sshd.service
6 | Wants=basic.target network-online.target
7 |
8 | [Service]
9 | ExecStart=/usr/bin/iperf3 --server
10 | SyslogIdentifier=iperf3
11 | #CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SETPCAP CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
12 | #CapabilityBoundingSet=CAP_NET_ADMIN
13 | RestrictNamespaces=yes
14 | ProtectClock=true
15 | ProtectSystem=strict
16 | ProtectHostname=yes
17 | ProtectHome=yes
18 | ProtectKernelTunables=yes
19 | ProtectKernelModules=yes
20 | ProtectControlGroups=yes
21 | SystemCallFilter=@system-service
22 | SystemCallErrorNumber=EPERM
23 | NoNewPrivileges=yes
24 | PrivateTmp=yes
25 | UMask=0077
26 | RestrictAddressFamilies=AF_NETLINK AF_INET AF_INET6
27 | DeviceAllow=/dev/null rw
28 | PrivateTmp=true
29 | ProtectSystem=true
30 | ProtectHome=true
31 | DynamicUser=true
32 | Restart=always
33 |
34 | [Install]
35 | WantedBy=multi-user.target
36 |
--------------------------------------------------------------------------------
/git-file-dates:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: set git repository files modification dates to the date
4 | # they were pushed
5 | # Author: Artem S. Tashkinov
6 | # Created at: Tue Sep 26 14:28:52 2023
7 | # Computer: zen
8 | # System: Linux 6.5.5-zen3 on x86_64
9 | #
10 | # Copyright (c) 2023 Artem S. Tashkinov All rights reserved.
11 | #
12 | #----------------------------------------------------------------------
13 |
14 | # otherwise bash under xargs won't find the function
15 | set -o allexport
16 |
17 | filesetdate()
18 | {
19 | unixtime=$(git log -1 --format="%at" -- "$1")
20 | touchtime=$(date -d @$unixtime +'%Y%m%d%H%M.%S')
21 | touch -t ${touchtime} "$1"
22 | }
23 |
24 | echo "Processing '$(pwd)' ..."
25 | # Let's hope there are no files/directories with newlines in their names
26 | git ls-tree -r --name-only HEAD | xargs -e -I {} -P $(nproc) bash -c 'filesetdate "{}"'
27 | echo "Done."
28 |
29 | # Or use parallel if it works (it may not if there are too many files)
30 | # parallel --keep-order --no-run-if-empty --null filesetdate "{}"
31 |
--------------------------------------------------------------------------------
/par2create-batch:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: create single-file par2 recovery record for each specified file
5 | # Author: Artem S. Tashkinov
6 | # Created at: Sat Jun 30 02:02:38 2018
7 | # Computer: localhost.localdomain
8 | # System: Linux 4.17.2-ic64 on x86_64
9 | #
10 | # Copyright (c) 2018 Artem S. Tashkinov All rights reserved.
11 | #
12 | #----------------------------------------------------------------------
13 |
14 | for i in "$@"; do
15 | echo "Processing $i ... "
16 | test -s "$i.rec.tar" && echo " PAR2 record already exists. Skipping" && continue
17 | echo -n " Creating PAR2 record ... "
18 | par2create -q -q -n1 -r1 "$i" && echo "OK" || exit 1
19 | echo -n " Verifying PAR2 record ... "
20 | par2verify -q -q "$i" && echo "OK" || exit 2
21 | md5sum "$i" "$i.par2" "$i.vol00+20.par2" > "$i.par2.md5" || exit 3
22 | touch -r "$i" "$i.par2" "$i.vol00+20.par2" "$i.par2.md5" || exit 4
23 | tar -cf "$i.rec.tar" "$i.par2" "$i.vol00+20.par2" "$i.par2.md5" || exit 5
24 | touch -r "$i" "$i.rec.tar" || exit 6
25 | /bin/rm "$i.par2" "$i.vol00+20.par2" "$i.par2.md5" || exit 7
26 | echo "Done"
27 | done
28 |
--------------------------------------------------------------------------------
/watts_cpu:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: show CPU package power consumption
5 | # Author: Artem S. Tashkinov
6 | # Created at: 2019-09-04 14:17:52
7 | # 2020-05-17 21:55:38 - output GPU wattage as well
8 | # 2022-04-14 09:24:31 - disable GPU wattage, don't show emoji for console
9 | # 2023-08-10 14:17:38 - adjust for updated AMD GPU sensors output
10 | #
11 | # Copyright (c) 2019-2023 Artem S. Tashkinov All rights reserved.
12 | #
13 | #----------------------------------------------------------------------
14 |
15 | test ! -e /dev/cpu/0/msr && echo "MSR module not loaded. Exiting" && exit 1
16 | test ! -r /dev/cpu/0/msr && echo "No permissions to read MSR. Exiting" && exit 2
17 |
18 | interval=1
19 | test -n "$1" && interval=$1
20 | # only show emoji for a graphical session
21 | ECPU="CPU "
22 | EGPU="GPU "
23 | test "$TERM" = "dumb" && ECPU="⚙️" && EGPU="🕹️"
24 | echo -n " $ECPU"
25 | turbostat --quiet --num_iterations 1 --interval "$interval" --show PkgWatt --Summary | tail -1 | tr -d '\n'
26 | echo -n "W"
27 |
28 | echo -n " $EGPU"
29 | sensors 'amdgpu-pci-*' 2>/dev/null | awk '/power|PPT/{print $2 "W"}'
30 |
--------------------------------------------------------------------------------
/dmesg-notify:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: show dmesg messages on the screen for modern Linux'es
5 | # Fined tuned for lxdm-session
6 | #
7 | # Author: Artem S. Tashkinov
8 | # Created at: Tue 10 Dec 2019 03:29:57 AM
9 | # Computer: localhost.localdomain
10 | # System: Linux 5.4.2-zen2 on x86_64
11 | #
12 | # Copyright (c) 2019 Artem S. Tashkinov All rights reserved.
13 | #----------------------------------------------------------------------
14 |
15 | icon=/path/to/icon_32x32.png
16 | timeout=3 # visual cue for this number of seconds
17 |
18 | nl='
19 | '
20 |
21 | journalctl -kf | while read -r line1; read -r -t 1 line2; read -r -t 1 line3; true; do
22 |
23 | test -n "$line2" && line1="$line1$nl"
24 | test -n "$line3" && line2="$line2$nl"
25 |
26 | pid_lxdm=`pidof lxdm-session`
27 | Xuser=0 # this might not work - needs to be tested
28 | test -z "$pid_lxdm" || Xuser=`ps -u --ppid "$pid_lxdm" | tail -1 | awk '{print $1}'`
29 |
30 | if [ -n "$Xuser" ]; then
31 | su "$Xuser" -c "DISPLAY=:0 notify-send --expire-time="${timeout}000" --icon="${icon}" 'Kernel message' '$line1$line2$line3' &> /dev/null" &
32 | fi
33 |
34 | done
35 |
--------------------------------------------------------------------------------
/ip-country:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # Show country code and IP address
4 | # 2018-04-25 12:37:31 - first release
5 | # 2024-08-31 20:17:00 - show real IP address as a tooltip (took 40 minutes to debug, getting old and stupid)
6 | # 2024-09-03 04:43:27 - i'm getting old and stupid
7 | # 2025-02-27 20:45:08 - ignore tun0's DOWN state
8 |
9 | fetcher()
10 | {
11 | # 🎭 👓
12 | test "$1" = "hasvpn" && note="🌍 " || note=""
13 | cleanup="cat"
14 |
15 | if [ "$1" = "direct" ]; then
16 | direct=direct
17 | cleanup="tail -n +2"
18 | fi
19 | data=`$direct curl --silent --max-time 10 https://ipapi.co/json/ 2>/dev/null | $cleanup`
20 | IP=`echo "$data" | jq .ip`
21 | country=`echo "$data" | jq .country`
22 | echo -n "$note$country:$IP" | sed 's/"//g'
23 | }
24 |
25 | tooltip()
26 | {
27 | echo "$*"
28 | }
29 |
30 | if jq --version &>/dev/null; then
31 | if [ -n "`ip l show tun0 2>/dev/null | grep DEFAULT | grep -v DOWN`" ]; then
32 | echo -n ""
33 | fetcher hasvpn
34 | echo ""
35 | tooltip `fetcher direct`
36 | else
37 | echo -n ""
38 | fetcher
39 | echo ""
40 | tooltip "Not using VPN"
41 | fi
42 | else
43 | echo "Install jq"
44 | fi
45 |
46 | echo "firefox https://myip.com"
47 |
--------------------------------------------------------------------------------
/firmware-updater.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: updates installed firmware files
4 | # Author: Artem S. Tashkinov
5 | # Created at: Fri Jan 17 02:00:30 2025
6 | # Computer: elite
7 | # System: Linux 6.12.6-200.fc41.x86_64 on x86_64
8 | #
9 | # Copyright (c) 2025 Artem S. Tashkinov All rights reserved.
10 | #
11 | #----------------------------------------------------------------------
12 |
13 | found=0
14 | items=0
15 |
16 | cd /lib/firmware || exit 1
17 | sdir=/tmp/linux-firmware
18 | test -n "$1" && sdir="$1"
19 | test ! -d "$sdir" && echo "$sdir doesn't exist" && exit 2
20 | test ! -f "$sdir/WHENCE" && echo "$sdir doesn't look like a directory with Linux firmware: WHENCE is missing" && exit 3
21 |
22 | while read fname; do
23 | cur=`sha256sum < $fname`
24 | new=`sha256sum 2>/dev/null < $sdir/$fname` || continue
25 | items=$((items+1))
26 | if [ "$cur" != "$new" ]; then
27 | echo -n "Updating $fname ... "
28 | /bin/cp -a "$sdir/$fname" "$fname" && echo OK || exit 100
29 | chown 0:0 "$fname"
30 | chmod 755 "$fname"
31 | found=$((found+1))
32 | fi
33 | done <<< "$(find . -type f)"
34 |
35 | echo "Scanned $items firmware files"
36 | test "$found" -eq 0 && echo "All the firmware is up to date" || echo "Updated $found firmware files"
37 |
--------------------------------------------------------------------------------
/monitor_overclock:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # Jun 2 03:00 AM 2019
4 |
5 | cvt --help || exit 1
6 |
7 | if [ -z "$3" ]; then
8 | echo "No parameters were given (`basename "$0"` width height freq), using defaults"
9 | # Adjust according to your needs
10 | width=1920
11 | height=1080
12 | freq=74
13 | else
14 | width=$1
15 | height=$2
16 | freq=$3
17 | fi
18 |
19 | echo "Width: $width; height: $height; refresh rate: $freq"
20 | name=${width}x${height}_${freq}.00
21 |
22 | test -n "`xrandr | grep $name`" && echo "$name is already added" && exit 1
23 |
24 | # Specifically for the first monitor
25 | output=`xrandr | awk '/ connected/{print $1}' | head -1`
26 | echo "Output: $output"
27 |
28 | # Adjust according to your needs
29 | mode=`cvt $width $height $freq | grep Modeline | sed 's/Modeline //;s/"//g'`
30 | echo "Modeline: $mode"
31 |
32 | echo -n "Creating a new mode $mode ... "
33 | xrandr --newmode $mode && echo OK || exit 1
34 |
35 | echo -n "Adding mode $name to $output ... "
36 | xrandr --addmode $output $name && echo OK || exit 1
37 |
38 | echo -n "Activating mode $name for $output ... "
39 | xrandr --output $output --mode $name && echo OK
40 |
41 | echo
42 | echo "Use these two commands to undo the changes (*after* setting the normal refresh rate):"
43 | echo "xrandr --delmode $output $name"
44 | echo "xrandr --rmmode 1920x1080_74.00"
45 |
--------------------------------------------------------------------------------
/check_installed_rpms:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | #----------------------------------------------------------------------
3 | # Description: a nifty script to verify all installed RPM
4 | #
5 | # Author: Artem S. Tashkinov <>
6 | # Created at: 2010-04-08 01:11:18
7 | #
8 | # Copyright (c) 2010 Artem S. Tashkinov. All rights reserved.
9 | #----------------------------------------------------------------------
10 |
11 | echo "Running ... "
12 |
13 | logf=/tmp/RPM-V.$RANDOM$RANDOM
14 | cat > $logf << EndOfMessage
15 | S file Size differs
16 | M Mode differs (includes permissions and file type)
17 | 5 digest (formerly MD5 sum) differs
18 | D Device major/minor number mismatch
19 | L readLink(2) path mismatch
20 | U User ownership differs
21 | G Group ownership differs
22 | T mTime differs
23 | P caPabilities differ
24 | EndOfMessage
25 |
26 | r[0]='-'
27 | r[1]='\'
28 | r[2]='|'
29 | r[3]='/'
30 | r[4]='-'
31 | r[5]='\'
32 | r[6]='|'
33 | r[7]='/'
34 |
35 | j=0
36 | n=1
37 |
38 | total=`rpm -qa | wc -l`
39 |
40 | for i in `rpm -qa | sort`; do
41 |
42 | p=$((n*100/total))
43 | echo -en "\r${r[$j]} $n [$p%]"
44 | RES=`nice -20 rpm -V "$i"`
45 |
46 | if [ -n "$RES" ]; then
47 | echo "******************** $i ********************" >> $logf
48 | echo "$RES" >> $logf
49 | fi
50 |
51 | n=$((n+1))
52 | j=$((j+1))
53 | test $j -gt 7 && j=0
54 |
55 | done
56 |
57 | echo
58 | echo "Done. $n packages verified. See log here: $logf"
59 |
--------------------------------------------------------------------------------
/uniunpack:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | # Universal Unpacker by Artem S. Tashkinov
4 | # v1.4.0 Oct 18 15:35 2014: total rewrite; proper gzip support; no command output :(
5 | # v1.4.1 Oct 26 13:20 2014: file extension normalized
6 | # v1.4.2 Nov 4 04:29 2014: use RAR by default, use -r- for RAR, BASH->SH
7 | # v1.4.3 Mar 7 22:02 2015: use UNRAR by default
8 | # v1.4.4 Tue 04 Nov 2019 01:18:27: prefer p7zip for zip archives
9 | # v1.4.5 Fri Oct 22 2021 15:38:59: zst
10 |
11 | test -z "$1" && echo "Need an archive name to proceed" && exit 1
12 | test ! -e "$1" && echo "File '$1' doesn't exist" && exit 1
13 |
14 | lower=$(echo "$1" | tr A-Z a-z)
15 |
16 | case "$lower" in
17 | *.tar|*.tbz|*.txz|*.tgz|*.tar.bz2|*.tar.gz|*.tar.xz|*.tar.lz|*.tar.zst)
18 | tar xf "$1"
19 | ;;
20 | *.rar)
21 | unrar x -r- "$1"
22 | ;;
23 | *.7z)
24 | 7z x "$1"
25 | ;;
26 | *.bz2)
27 | bzip2 -kd "$1"
28 | ;;
29 | *.gz)
30 | gunzip -k "$1"
31 | ;;
32 | *.xz)
33 | xz -dk "$1"
34 | ;;
35 | *.zip)
36 | if 7z &> /dev/null; then
37 | 7z x "$1"
38 | else
39 | unzip "$1"
40 | fi
41 | ;;
42 | *.cpio)
43 | cpio -i -m -d -F "$1"
44 | ;;
45 | *.zst)
46 | zstd -d "$1" # --keep/-k is default
47 | ;;
48 | *)
49 | echo "The archive type for '$1' is unknown"
50 | echo -n "Would you like to try unpacking the archive using 7z (y/n)? "
51 | read answer
52 | test "$answer" = "Y" -o "$answer" = "y" && 7z x "$1"
53 | ;;
54 | esac
55 |
56 | res=$?
57 | test "$res" -ne 0 && echo "WARNING: Unpacking failed!"
58 | exit "$res"
59 |
--------------------------------------------------------------------------------
/defrag:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: fast defrag files in the specified directory for ext4
4 | # Author: Artem S. Tashkinov
5 | # Created at: Fri Jun 10 10:11:00 2022
6 | # 2023-04-22 13:45:01 egrep: warning: egrep is obsolescent; using grep -E
7 | # System: Linux 5.18.3-az3 on x86_64
8 | #
9 | # Copyright (c) 2022 Artem S. Tashkinov All rights reserved.
10 | #
11 | #----------------------------------------------------------------------
12 |
13 | # Initially used this but it takes forever to complete:
14 | #
15 | # find . -xdev -type f | while read filename; do filefrag "$filename" | egrep -v ": 1 extent|: 0 extents"; done | while read line; do fname=`echo "$line" | awk -F ': ' '{print $1}'`; e4defrag -v "$fname" | grep -v "e4defrag 1"; done
16 | #
17 | # -P $(nproc) below could be too much or too little for your system - adjust accordingly
18 |
19 | isfragged()
20 | {
21 | for i in "$@"; do
22 | filefrag "$i" | grep -E -v ": 1 extent|: 0 extents"
23 | done
24 | }
25 |
26 | if [ "$1" = "isfragged" ]; then
27 | shift
28 | isfragged "$@"
29 | exit 0
30 | fi
31 |
32 | test "$UID" -ne "0" && echo "Must be run under root" && exit 0
33 |
34 | test ! -d "$1" && echo "Need a valid directory to continue" && exit 1
35 | cd "$1" || exit 2
36 | echo "Defragging [$1] ..."
37 | find . -xdev -type f -print0 | xargs -r -0 -P $(nproc) defrag isfragged | while read line; do fname=`echo "$line" | awk -F ': ' '{print $1}'`; e4defrag -v "$fname" | grep -v "e4defrag 1"; done
38 |
--------------------------------------------------------------------------------
/avoid-vpn:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: avoid VPN for certain either predefined or given hosts
5 | # Author: Artem S. Tashkinov
6 | # Created at: Mon Jan 1 22:09:05 2018
7 | # Thu Jan 31 15:40:07 2019 exclude tun when looking for a defgw
8 | # 2019-08-27 14:15:16 - no idea what it was
9 | # Wed 16 Oct 2019 08:58:46 PM - beautify IP using printf
10 | # 2022-07-18 16:37:59 - print debug only in the interactive console
11 | #
12 | # Computer: localhost.localdomain
13 | # System: Linux 4.14.10-ic64 on x86_64
14 | #
15 | # Copyright (c) 2018 Artem S. Tashkinov All rights reserved.
16 | #----------------------------------------------------------------------
17 |
18 | eko()
19 | {
20 | test "$TERM" = "linux" -o "$TERM" = "xterm-256color" && echo "$@"
21 | }
22 |
23 | default=( "irc.freenode.net" "en.wikipedia.org" "distrowatch.com" "wiki.debian.org" "irc.gnome.org" )
24 | (( $# == 0 )) && set -- "${default[@]}" # Set arguments to $default if no command line arguments are given
25 | defgw=`ip r | grep -v tun | awk '/default via/{print $3}'`
26 | test -z "$defgw" && eko "Default gateway is not defined" && exit 1
27 | eko "Default gateway is $defgw"
28 |
29 | for i in "$@"; do
30 | eko "Routing $i ... "
31 | for IP in `host "$i" | awk '/has address/{print $4}'`; do
32 | printf " %-14s : " "$IP"
33 | if [ -z "`ip r | grep -w "$IP"`" ]; then
34 | ip r a "$IP/32" via "$defgw" && eko "OK" || eko "failed"
35 | else
36 | eko "already routed"
37 | fi
38 | done
39 | done
40 |
--------------------------------------------------------------------------------
/dmesg-notify-user:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: show dmesg messages on the screen for modern Linux'es
5 | # Fined tuned for lxdm-session
6 | #
7 | # Author: Artem S. Tashkinov
8 | # Created at: Tue 10 Dec 2019 03:29:57 AM
9 | # 2020-03-07 18:34:57 - skip FW messages
10 | # 2020-04-05 01:18:14 - fix grep taking forever to process input (add --line-buffered)
11 | # Computer: localhost.localdomain
12 | # System: Linux 5.4.2-zen2 on x86_64
13 | #
14 | # Copyright (c) 2019 Artem S. Tashkinov All rights reserved.
15 | #----------------------------------------------------------------------
16 |
17 | icon=/usr/share/icons/gnome/48x48/status/dialog-warning.png
18 | #git
19 | #icon=/path/to/icon_32x32.png
20 | timeout=3 # visual cue for this number of seconds
21 |
22 | nl='
23 | '
24 |
25 | journalctl -kf | grep --line-buffered --invert-match "FW-INGRESS" | while read -r line1; read -r -t 1 line2; read -r -t 1 line3; true; do
26 |
27 | test -n "$line2" && line1="$line1$nl"
28 | test -n "$line3" && line2="$line2$nl"
29 |
30 | # pid_lxdm=`pidof lxdm-session`
31 | # Xuser=0 # this might not work - needs to be tested
32 | # test -z "$pid_lxdm" || Xuser=`ps -u --ppid "$pid_lxdm" | tail -1 | awk '{print $1}'`
33 |
34 | # if [ -n "$Xuser" ]; then
35 | # su "$Xuser" -c "DISPLAY=:0 notify-send --expire-time="${timeout}000" --icon="${icon}" 'Kernel message' '$line1$line2$line3' &> /dev/null" &
36 | # fi
37 |
38 | notify-send --expire-time="${timeout}000" --icon="${icon}" "Kernel message" "$line1$line2$line3" &> /dev/null &
39 |
40 | done
41 |
--------------------------------------------------------------------------------
/metaclone:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | # Exit if any command fails
4 | set -e
5 |
6 | # 2012-10-03 05:51:23
7 | # a script to create a meta copy of the required directory
8 |
9 | src="$1"
10 | test -z "$src" -o "${src:0:1}" != "/" && echo "Usage is: `basename $0` directory_full_path" && exit
11 | test "$(ls "$src")" = "$(ls /)" && echo "I will not clone the root folder, sorry" && exit
12 |
13 | test ! -d "$src" && "'$src' is not a valid directory"
14 | cd "$src"
15 |
16 | tempdir=$(mktemp -d)
17 | basedir=$(basename "$src")
18 | dst=$tempdir/$basedir
19 | mkdir "$dst"
20 |
21 | mdsync()
22 | {
23 | touch --reference="$1" "$2"
24 | chown --reference="$1" "$2"
25 | chmod --reference="$1" "$2"
26 | }
27 |
28 | echo "Creating directories ..."
29 | find . -depth -type d -exec mkdir -p "$dst"/\{\} \;
30 |
31 | echo "Cloning files ..."
32 | find . -type f | while read filename; do
33 | test -f "$dst/$filename" && echo "$dst/$filename already exists. This should have never happened. Bailing out!" && exit 100
34 | fsize=`stat -c '%s' "$filename"`
35 |
36 | if [ $fsize -gt 0 ]; then
37 | fallocate -o $((fsize-1)) -l 1 "$dst/$filename" || exit
38 | fi
39 |
40 | mdsync "$filename" "$dst/$filename"
41 | done
42 |
43 | echo "Cloning directories dates/owners/accesses ..."
44 | find . -depth -type d | while read filename; do
45 | mdsync "$filename" "$dst/$filename"
46 | done
47 |
48 | echo "All done"
49 |
50 | cd "$tempdir"
51 |
52 | test -f "$basedir.tar" && echo "$basedir.tar already exists. This should have never happened. Bailing out!" && exit 100
53 | tar -Scf "$basedir.tar" "$basedir"
54 | echo
55 | echo "The resulting file can be located at $tempdir/$basedir.tar"
56 | echo "$tempdir/$basedir can be safely deleted"
57 |
--------------------------------------------------------------------------------
/direct:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # This is for VPN users: oftentimes you need to access certain websites directly while bypassing VPN and you don't want to disable it.
4 | # This script allows you to run any application, so that it talked to the Internet from your primary interface, not your VPN interface.
5 | # This requires https://github.com/Intika-Linux-Firewall/Bind-Interface-IP
6 | # Compile both for the i686 and x86-64 architectures and this script will work for an application regardless of its bitness
7 | #
8 | # 2022-08-23 15:20:09 bloody tired of Google who hates VPN
9 | # 2022-11-07 20:39:28 add `exec` to avoid an extra hanging bash process
10 | # 2022-12-22 10:47:51 UTC updated description
11 |
12 | ifdirect=`ip -br link show | awk '/^en/{print $1}'`
13 | ipdirect=`ip -4 -br addr show dev "$ifdirect" | awk '{print $3}' | awk -F '/' '{print $1}'`
14 |
15 | echo "Using IP adress $ipdirect ..."
16 | test -z "$1" && echo "Need a command to run :-)" && exit 1
17 | export BIND_ADDR="$ipdirect"
18 | export LD_PRELOAD="/usr/local/lib/bind.so /usr/local/lib64/bind.so"
19 | exec "$@"
20 |
21 | # This requires some preparation
22 |
23 | # 1. For rc.local
24 | ## v2 working 2020-11-24 15:34:04
25 | #direct2()
26 | #{
27 | # grep -q "200 direct" /etc/iproute2/rt_tables || echo -e "\n# `date`\n200 direct" >> /etc/iproute2/rt_tables
28 | # ifdirect=`ip l | grep " en" | awk -F : '{print $2}' | sed 's/ //'`
29 | # if [ -n "$ifdirect" ]; then
30 | # ip rule add from 192.168.0.1/24 table direct prio 1
31 | # ip route add default via 192.168.0.1 dev "$ifdirect" table direct
32 | # fi
33 | #}
34 |
35 | # 2. For /etc/iproute2/rt_tables
36 | ## Tue Nov 24 03:24:42 PM 2020
37 | # 200 direct
38 |
39 | # 3. And lastly
40 | # https://github.com/Intika-Linux-Firewall/Bind-Interface-IP
41 |
--------------------------------------------------------------------------------
/mouse-acceleration-toggle:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: toggle or change mouse acceleration using libinput
5 | # Wed Jan 23 00:25:46 2019 : on/off argument
6 | # Author: Artem S. Tashkinov
7 | # Created at: Sun Jan 20 18:26:25 2019
8 | # 2019-01-23 00:31:42 changes unknown
9 | # 2022-10-21 01:54:22 fixes for new xinput output
10 | # Computer: localhost.localdomain
11 | # System: Linux 4.19.15-300.fc29.x86_64 on x86_64
12 | #
13 | # Copyright (c) 2019-2022 Artem S. Tashkinov All rights reserved.
14 | #
15 | #----------------------------------------------------------------------
16 |
17 | test -z "$DISPLAY" && echo "DISPLAY is not set. Exiting" && exit -1
18 |
19 | # sed 's/.*id=\(.\).*/\1/'` - this should have never worked
20 | mouse=`xinput list | grep Mouse | grep -v Keyboard | sed 's/.*id=\([0-9]*\).*/\1/'`
21 | # was "Accel Profile Enabled (303)" - now 304, why? Let's remove the number
22 | current=`xinput --list-props "$mouse" | grep -i "Accel Profile Enabled (" | rev | cut -c -4 | rev` # alternatively tail -c 5
23 |
24 | w=`echo "$1" | tr '[:upper:]' '[:lower:]'`
25 |
26 | echo -n "[$current]"
27 |
28 | if [ "$current" = "1, 0" ]; then
29 | echo -n " Mouse acceleration is ENABLED. "
30 | test "$w" = "on" && echo "" && exit 0
31 | status="on"
32 | else
33 | echo -n " Mouse acceleration is disabled. "
34 | test "$w" = "off" && echo "" && exit 0
35 | status="off"
36 | fi
37 |
38 | if [ "$status" = "on" -o "$w" = "off" ]; then
39 | echo -n "Disabling ... "
40 | xinput --set-prop "$mouse" 'libinput Accel Profile Enabled' 0, 1 && echo OK
41 | elif [ "$status" = "off" -o "$w" = "on" ]; then
42 | echo -n "ENABLING ... "
43 | xinput --set-prop "$mouse" 'libinput Accel Profile Enabled' 1, 0 && echo OK
44 | fi
45 |
--------------------------------------------------------------------------------
/audio-conf:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # 2022-04-01 11:44:22 UTC
4 | # Load the TCP module to allow other local users to use audio devices
5 | # Change the default sampling rate to 48KHz
6 | # Enable more allowed sampling rates
7 |
8 | # https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PulseAudio#module-native-protocol-tcp
9 | # "auth-ip-acl=127.0.0.1;192.168.1.0/24;::1" https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/847#note_828707
10 | # "listen=127.0.0.1;192.168.0.100;::1" https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/2259
11 | #
12 | # 2022-04-04 11:44:29 UTC
13 | # + module-x11-bell : /usr/share/sounds/freedesktop/stereo/bell.oga in sound-theme-freedesktop
14 | # 2022-12-22 10:27:26 UTC
15 | # + module-combine-sink : output to all enabled devices simultaneously (that's an output virtual "combined" device)
16 | # 2022-12-30 09:29:33 UTC
17 | # - module-combine-sink : wireplumber makes it default all the time and that fucks up volume management
18 |
19 | declare -A opts
20 | opts[module-native-protocol-tcp]="listen=127.0.0.1"
21 | opts[module-combine-sink]="sink_name=CombinedVirtual"
22 |
23 | for i in module-native-protocol-tcp module-x11-bell; do # module-combine-sink
24 | if [ -z "`pactl list modules | grep $i`" ]; then
25 | echo -n "Loading $i ... "
26 | pactl load-module $i ${opts[$i]} && echo OK
27 | else
28 | echo "$i is already loaded"
29 | fi
30 | done
31 |
32 | # https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Config-PipeWire
33 | # Get settings: `pw-metadata -n settings`
34 |
35 | if ! which pw-metadata &> /dev/null; then
36 | echo "Error: 'pw-metadata' is missing, please install pipewire-utils"
37 | exit 1
38 | fi
39 |
40 | echo "Setting extra options ... "
41 | pw-metadata -n settings 0 clock.rate 48000
42 | pw-metadata -n settings 0 clock.allowed-rates '[ 48000, 96000, 44100 ]'
43 |
--------------------------------------------------------------------------------
/tar_sorted:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 |
3 | #----------------------------------------------------------------------
4 | # Description: Create tar archive with files sorted by extension
5 | # Author: Artem S. Tashkinov
6 | # Created at: 2010-06-15 18:51:19
7 | # 2014-04-23 02:53:33 (--numeric-owner)
8 | # 2010-07-18 22:27:07 unknown fixes
9 | # 2019-09-10 16:21:14 fixed "if" - must have used "test"
10 | # 2023-02-23 14:05:52 a fix for filenames containing '\' - should this be applied to directories names as well?
11 | # 2023-07-02 14:21:04 simplify
12 | # Computer: localhost.localdomain.
13 | # System: Linux 2.6.33.4-ic on i686
14 | #
15 | # Copyright (c) 2010 Artem S. Tashkinov All rights reserved.
16 | #----------------------------------------------------------------------
17 |
18 | # set -x
19 |
20 | [ ! -d "$1" ] && echo "Gimme an existing directory name" >&2 && exit 1
21 | [ -z "$2" ] && echo "Give me a destination filename" >&2 && exit 2
22 | [ -f "$2" ] && echo "[$2] already exists!" >&2 && exit 3
23 |
24 | [ -n "`find "$1" -iname '*|*'`" ] && echo "birdie is stupid :( - there are files with '|' in their names! This won't work." >&2 && exit 4
25 |
26 | find_files()
27 | {
28 | #find "$1"-type f | sed 's/^\.\///;s/\(..*\)\./\1\|/' | sort -t '|' -k2 | sed 's/|/\./'
29 | # Thanks to Neil Moore aka \ametyst at irc://irc.freenode.net/bash
30 | find "$1" ! -type d | sed -e 's,\(.*\)/,\1|,' -e 's,\(.*|..*\)\.,\1|,' | sort -i -t\| -k 3,3 -k 2,2 --buffer-size=1G | sed -e 's,|,/,' -e 's,|,.,' | sed 's/\\/\\\\/g'
31 | test "${PIPESTATUS[0]}" != "0" && return 1
32 | find "$1" -depth -type d || return 1 # Saving directories timestamps, you SHOULD run tar with --no-recursion option
33 | }
34 |
35 | filelist=`mktemp` || exit 5
36 | find_files "$1" > "$filelist" || exit 6
37 |
38 | tar --no-recursion --files-from="$filelist" -acf "$2" # --numeric-owner
39 | /bin/rm "$filelist"
40 |
--------------------------------------------------------------------------------
/message_gui_user:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: send a notification to a currently logged in user in X.org
5 | # Author: Artem S. Tashkinov
6 | # Created at: Thu Jan 16 22:57:14 2020
7 | # It took me three hours to write and debug it though it looks simple
8 | # Computer: zen
9 | # System: Linux 5.4.8-az2 on x86_64
10 | #
11 | # Copyright (c) 2020 Artem S. Tashkinov All rights reserved.
12 | #
13 | #----------------------------------------------------------------------
14 |
15 | msg_icon=/path/to/icon_32x32.png
16 |
17 | test -z "$3" && echo "Usage is `basename "$0"` timeout(seconds) title message" && exit 1
18 | timeout=$1
19 |
20 | # message type modern/classic
21 | mstype=modern
22 |
23 | if ! [[ "$1" =~ ^[0-9]+$ ]]; then
24 | echo "$1 is not an integer number."
25 | exit 1
26 | fi
27 |
28 |
29 | whos() # print users whose sessions start with ":" i.e Xorg users
30 | {
31 | who | awk '{if ($2~/^:/) print}'
32 | }
33 |
34 | message() {
35 | echo "Detected user:$1 DISPLAY=$2. Attempting to show a $mstype message ..."
36 | if [ "$mstype" = "classic" ]; then
37 | su "$1" -c "DISPLAY=$2 xmessage -center -timeout $timeout -title '$3' '$4'"
38 | else
39 | su "$1" -c "DISPLAY=$2 notify-send --expire-time="${timeout}000" --icon="$msg_icon" '$3' '$4'"
40 | fi
41 | }
42 |
43 | pid_lxdm=`pidof lxdm-session`
44 | test -n "$pid_lxdm" && Xuser=`ps --no-headers -u --ppid "$pid_lxdm" | awk '{print $1}'`
45 |
46 | if [ -n "$Xuser" ]; then
47 | # LXDM version
48 | echo "LXDM session detected ..."
49 | Xdisp=`ps --no-headers -u --ppid "$pid_lxdm" | awk '{print $2}'`
50 | Xdisp=`tr '\0' '\n' < /proc/$Xdisp/environ | awk -F = '/^DISPLAY=/{print $2}'`
51 | message "$Xuser" "$Xdisp" "$2 for ${timeout}sec" "$3"
52 | elif [ -n "`whos`" ]; then
53 | # Universal version in case w does work (it doesn't for LXDM)
54 | echo "who is working :)"
55 | whos | while read Xuser Xdisp; do
56 | message "$Xuser" "$Xdisp" "$2 for ${timeout}sec" "$3"
57 | done
58 | else
59 | echo "Attemptin to show a display login manager message ..."
60 | message "root" ":0" "$2 for ${timeout}sec" "$3"
61 | fi
62 |
--------------------------------------------------------------------------------
/ryzen-powersave.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # 2023-08-07 00:46:52 - created
4 | # 2023-08-10 14:15:30 - lots more limits
5 | # 2023-08-12 20:11:58 - let's add a delay because --power-saving doesn't always work otherwise
6 | # 2023-08-21 20:16:20 - tune for console and rc.d
7 | # 2023-11-02 09:25:35 - setting for running on battery power
8 | # 2023-11-16 15:45:27 - rewrite
9 | # 2023-11-24 20:06:52 - dump the defaults to /dev/shm/AMD
10 | # 2023-11-29 09:53:29 - remove --power-saving, it breaks the CPU up on resume
11 | # 2023-12-29 22:25:19 - restore --power-saving, firmware is broken regardless
12 | # 2024-02-11 18:00:14 - since 6.7 /sys/class/graphics/fb0 is missing, adjusting accordingly
13 | # 2024-03-18 01:10:36 - drop wattage limits, temperature is enough
14 | delay=60
15 |
16 | binary=/usr/local/bin/ryzenadj
17 |
18 | gpupower()
19 | {
20 | gpl=device/power_dpm_force_performance_level
21 | gpudev=/sys/class/graphics/fb0/$gpl
22 | test -f $gpudev || gpudev=/sys/class/drm/card1/$gpl
23 | test -f $gpudev || gpudev=/sys/class/drm/card0/$gpl
24 | if [ -f $gpudev ]; then
25 | echo "$1" > $gpudev && ( echo -n "Setting GPU performance to: " && cat $gpudev ) || echo "Failed!"
26 | else
27 | echo "iGPU [ $gpudev ] is missing!"
28 | fi
29 | }
30 |
31 | conf()
32 | {
33 | if [ -z "$1" ]; then
34 | echo "Mains settings:"
35 | $binary --tctl-temp=80 --power-saving 2>&1 | grep -v SMU
36 | gpupower auto
37 | elif [ "$1" = "battery" ]; then
38 | echo "Battery settings:"
39 | $binary --tctl-temp=70 --power-saving --stapm-limit=15000 --fast-limit=15000 --slow-limit=10000 2>&1 | grep -v SMU
40 | echo "Enabling GPU low power, this limits RAM speed!"
41 | gpupower low
42 | elif [ "$1" = "fast" ]; then
43 | echo "Fast and hot settings:"
44 | $binary --tctl-temp=85 2>&1 | grep -v SMU
45 | gpupower auto
46 | fi
47 |
48 | true
49 | }
50 |
51 | test -f /dev/shm/AMD || $binary --info &> /dev/shm/AMD
52 |
53 | echo "Enabling power savings via ryzenadj and /sys ..."
54 | conf "$1"
55 |
56 | if [ "$TERM" = "linux" -o "$TERM" = "xterm-256color" ]; then
57 | :
58 | else
59 | echo "Second attempt in $delay seconds ..."
60 | ( sleep $delay; conf ) &
61 | fi
62 |
--------------------------------------------------------------------------------
/clean_dnf:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: script for rpm uninstallation within all installed rpms
4 | #
5 | # Author: Artem S. Tashkinov
6 | # Created at:
7 | # Mon Nov 21 20:12:04 2016 - dnf, show deps, no clear
8 | # Wed Dec 13 19:43:00 2017 - remove by mask, skip already deleted, skip protected
9 | # Fri May 11 18:52:51 2018 - skip packages with errors instead of waithing for the user input
10 | # stop background dependancy checking
11 | # set env var SKIP to skip up to a certain package
12 | #
13 | # Copyright (c) 2004 Artem S. Tashkinov. All rights reserved.
14 | #----------------------------------------------------------------------
15 |
16 | #set -x
17 | dont_remove="|AdobeReader_enu|Thunar|icewm|hexchat|xterm|xfce4|qbittorrent|mkvtoolnix|rpm-build|wireshark|samba|libxml2-devel|libvorbis|libogg|alsa|lxdm|xfdesktop|openssh|vpx|google-chrome|mesa-lib|libxslt|openssl|pulseaudio-libs|electrum|firewall"
18 |
19 | clear
20 |
21 | resolve()
22 | {
23 | name=`rpm -q --queryformat "%{NAME}" "$1"`
24 | result=`dnf -C --setopt=clean_requirements_on_remove=False --assumeno remove "$1" 2>&1` # | grep -v "Operation aborted."`
25 | protect=`echo "$result" | egrep "protected packages$dont_remove"`
26 | if [ -z "$protect" ]; then
27 | myself=`echo "$result" | grep -vw "$name" | awk '/@/{print $1"-"$3"."$2}'` # awk: don't print the package itself as a dependancy: if (index("'"$1"'",$1)) next;
28 | if [ -z "$myself" ]; then
29 | echo "[OK]: no dependencies found."
30 | else
31 | echo -n "[WARN]: Dependent on $1: "
32 | echo "$result" | grep -vw "$name" | awk 'BEGIN{ORS=", "}/@/{print $1}' | sed 's/, $/\n/'
33 | fi
34 | echo -n "Remove (n)? "
35 | else
36 | echo -e "[ERROR]:\n$protect"
37 | return 1
38 | fi
39 | }
40 |
41 | some="."
42 | test -n "$1" && some="$1"
43 |
44 | for i in `rpm -qa | sort | grep "$some"`; do
45 | [[ "$i" < "$SKIP" ]] && continue
46 | # clear
47 | if rpm -qi "$i"; then
48 | if resolve "$i"; then # &
49 | read answer
50 | if [ "$answer" == "Y" -o "$answer" == "y" ]; then
51 | dnf -C remove "$i"
52 | fi
53 | else
54 | echo "[ERROR]: skipping a protected package"
55 | fi
56 | else
57 | echo "[WARNING]: Package "$i" has already been removed"
58 | fi
59 | echo "________________________________________________________________"
60 | done
61 |
--------------------------------------------------------------------------------
/alert-bad-processes-user:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: alert audibly and visually if there are CPU heavy
5 | # processes running
6 | # if argument1 is set then alert audibly
7 | #
8 | # 2026-01-11 23:11:09 v1.0.1 - made `top` version agnostic
9 | # 2019-10-16 21:31:20 fine tune for LXDM *only*
10 | # 2025-01-02 05:25:38 turned it into a user script to be run from ~/.config/autostart; fixed top's no config invocation (no option yet, filed a bug report)
11 | # 2025-01-03 05:25:38 now uses a standard audio file
12 | #
13 | # Author: Artem S. Tashkinov
14 | # Created at: Mon Jan 11 18:54:14 2016
15 | # Computer: localhost.localdomain
16 | # System: Linux 4.4.0-ic64 on x86_64
17 | #
18 | # Copyright (c) 2016-2025 Artem S. Tashkinov All rights reserved.
19 | #----------------------------------------------------------------------
20 |
21 | faudio=/usr/share/sounds/freedesktop/stereo/complete.oga
22 | timeout=3 # visual cue for this number of seconds
23 | threshold=60 # minimum CPU usage which is considered bad
24 | volume=33 # audio notification volume
25 | interval=5 # check for bad processes every X seconds
26 |
27 | nl='
28 | '
29 |
30 | processes()
31 | {
32 | # doesn't work: shows the aggregated CPU usage which might be very low for long running processes
33 | # ps auxS | awk '{if ($3 > 80) for (i=11; i<=NF; i++) print $i" "}'
34 |
35 | # doesn't work: top version specific
36 | # HOME=/dev/null top -bn1 | tail -n +8 | awk 'BEGIN{ORS=""}{if ($9 > '$threshold') {print $9"%\t"; for (i=12; i<=NF; i++) print $i" "; print "\n"}}'
37 |
38 | # avoid using user's top settings
39 | env HOME=/dev/null XDG_CONFIG_HOME="" top -bn1 | \
40 | awk '
41 | BEGIN {ORS=""}
42 | {
43 | if ($0~/PID.*USER.*PR/) {
44 | for (i=1;i<=NF;i++) {
45 | if ($i~/CPU/) f_cpu=i;
46 | if ($i~/COMMAND/) f_cmd=i;
47 | found=1;
48 | }
49 | }
50 | if (found) {
51 | if ($f_cpu > '$threshold') {
52 | print $f_cpu"%\t";
53 | for (i=f_cmd; i<=NF; i++) {
54 | if ($i != "`-") # top 3.3 likes a tree structure
55 | print $i" ";
56 | }
57 | print "\n";
58 | }
59 | }
60 | }'
61 | }
62 |
63 | while :; do
64 | bad_processes=`processes`
65 |
66 | if [ -n "$bad_processes" ]; then
67 |
68 | notify-send --expire-time="${timeout}000" --icon=/mnt/STORAGE/birdie/linux/new/Adwaita_32x32_dialog-warning.png "Warning!" "Detected > ${threshold}%:$nl$bad_processes" &> /dev/null &
69 |
70 | if [ -f "$faudio" -a -n "$1" ]; then
71 | paplay --volume=$((volume*65535/100)) "$faudio" &
72 | fi
73 |
74 | fi
75 | sleep $interval
76 | done
77 |
--------------------------------------------------------------------------------
/alert-bad-processes:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: alert audibly and visually if there are CPU heavy
5 | # processes running
6 | # if argument1 == 1 then alert audibly
7 | # if argument2 == 1 then alert visually
8 | #
9 | # Mon Jan 11 23:11:09 2016 v1.0.1 - made `top` version agnostic
10 | # Wed 16 Oct 2019 09:31:20 PM - fine tune for LXDM *only*
11 | #
12 | # Author: Artem S. Tashkinov
13 | # Created at: Mon Jan 11 18:54:14 2016
14 | # Computer: localhost.localdomain
15 | # System: Linux 4.4.0-ic64 on x86_64
16 | #
17 | # Copyright (c) 2016 Artem S. Tashkinov All rights reserved.
18 | #----------------------------------------------------------------------
19 |
20 | faudio=/opt/kde3/share/sounds/KDE_Beep_ClockChime.wav
21 | timeout=3 # visual cue for this number of seconds
22 | threshold=75 # minimum CPU usage which is considered bad
23 |
24 | nl='
25 | '
26 |
27 | processes()
28 | {
29 | # doesn't work: shows the aggregated CPU usage which might be very low for long running processes
30 | # ps auxS | awk '{if ($3 > 80) for (i=11; i<=NF; i++) print $i" "}'
31 |
32 | # doesn't work: top version specific
33 | # HOME=/dev/null top -bn1 | tail -n +8 | awk 'BEGIN{ORS=""}{if ($9 > '$threshold') {print $9"%\t"; for (i=12; i<=NF; i++) print $i" "; print "\n"}}'
34 |
35 | # avoid using user's top settings
36 | HOME=/dev/null top -bn1 | \
37 | awk '
38 | BEGIN {ORS=""}
39 | {
40 | if ($0~/PID.*USER.*PR/) {
41 | for (i=1;i<=NF;i++) {
42 | if ($i~/CPU/) f_cpu=i;
43 | if ($i~/COMMAND/) f_cmd=i;
44 | found=1;
45 | }
46 | }
47 | if (found) {
48 | if ($f_cpu > '$threshold') {
49 | print $f_cpu"%\t";
50 | for (i=f_cmd; i<=NF; i++) {
51 | if ($i != "`-") # top 3.3 likes a tree structure
52 | print $i" ";
53 | }
54 | print "\n";
55 | }
56 | }
57 | }'
58 | }
59 |
60 | if [ "$1" = 1 ]; then
61 | if [ -n "`processes`" ]; then
62 | if [ -f "$faudio" ]; then
63 | aplay -q "$faudio" &
64 | elif eplay --version &>/dev/null; then
65 | play -q -n synth 0.5 sine &
66 | else
67 | echo -ne '\007' &
68 | fi
69 | fi
70 | fi
71 |
72 | pid_lxdm=`pidof lxdm-session`
73 | Xuser=0 # this might not work - needs to be tested
74 | test -z "$pid_lxdm" || Xuser=`ps -u --ppid "$pid_lxdm" | tail -1 | awk '{print $1}'`
75 |
76 | if [ "$2" = 1 -a -n "$Xuser" ]; then
77 | bad_processes=`processes`
78 | if [ -n "$bad_processes" ]; then
79 | su "$Xuser" -c "DISPLAY=:0 notify-send --expire-time="${timeout}000" --icon=/mnt/STORAGE/birdie/linux/new/Adwaita_32x32_dialog-warning.png 'Warning!' 'Detected > ${threshold}%:$nl$bad_processes' &> /dev/null" &
80 | fi
81 | fi
82 |
--------------------------------------------------------------------------------
/watch_raw_io:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: watch raw input output per block device stats in real time
5 | # Author: Artem S. Tashkinov
6 | # Created at: Sat Apr 6 18:34:33 2013
7 | # Computer: localhost.localdomain
8 | # System: Linux 3.8.7-ic on i686
9 | #
10 | # Copyright (c) 2013 Artem S. Tashkinov All rights reserved.
11 | # Sat Aug 24 19:59:32 2013 - implemented read/write interval counters
12 | # Sun Aug 25 23:51:14 2013 - made refreshing three times faster
13 | # Thu Feb 4 18:59:41 2016 - exit on pressing q/Q, read refresh rate from console
14 | # 2020-03-27 18:04:40 - fix finding the root in /proc/cmdline - remove an extra space, add -w instead
15 | # - sort devices numerically
16 | # 2021-10-23 04:51:04 - add nvme support
17 | #
18 | #----------------------------------------------------------------------
19 |
20 | shopt -s nullglob
21 | export LANG=en_US.UTF-8
22 | rr=2
23 |
24 | trap "exit 0" TERM
25 | export TOP_PID=$$
26 |
27 | qexit()
28 | {
29 | read -n 1 -t 0.5 -s char
30 | if [ "$char" = "q" -o "$char" = "Q" ]; then
31 | echo "Bye"
32 | kill -s TERM $TOP_PID
33 | fi
34 | }
35 |
36 | if [ -n "$1" ]; then
37 | if [ "$1" -eq "$1" ] 2>/dev/null; then
38 | rr="$1"
39 | else
40 | echo "[$1] is not a number, setting refresh rate to $rr"
41 | sleep 3
42 | fi
43 | fi
44 |
45 | dsrc=/sys/block
46 | cd $dsrc || die "$dsrc not found"
47 |
48 | declare -A intr intw blkr blkw
49 |
50 | while :; do
51 | clear
52 | date | tr -d '\n'
53 | echo " [ refresh rate: ${rr} seconds ]"
54 | echo "Device Read bytes Written bytes Interval read Interval written Mount point"
55 | for i in sd* md* nvme*; do # adjust for your environment
56 | # for j in $i/$i* $i; do
57 | for j in $i/$i[0-9] $i/$i[0-9][0-9] $i/$i[0-9][0-9][0-9] $i/nvme*n*p* $i; do # at most 999 partitions
58 | dev=${j##*/}
59 | test -z "$dev" && dev="$j" # when iterating $i itself
60 |
61 | read blkr blkw < <(awk '{print $3" "$7}' $j/stat) # let's speed up this a bit
62 | blkr["$dev"]=$blkr
63 | blkw["$dev"]=$blkw
64 |
65 | test -z "${intr[$dev]}" && intr[$dev]=0
66 | test -z "${intw[$dev]}" && intw[$dev]=0
67 | intr[$dev]=$((${blkr[$dev]}-${intr[$dev]}))
68 | intw[$dev]=$((${blkw[$dev]}-${intw[$dev]}))
69 | test "${blkr[$dev]}" == "${intr[$dev]}" && intr[$dev]=0 # Don't show when iterating for the first time
70 | test "${blkw[$dev]}" == "${intw[$dev]}" && intw[$dev]=0
71 |
72 | printf "%-10s %'17d %'17d %'14d %'14d " $dev $((blkr[$dev]*512)) $((blkw[$dev]*512)) $((intr[$dev]*512)) $((intw[$dev]*512))
73 |
74 | mntp=`awk '/^\/dev\/'$dev' /{print $2}' /proc/mounts`
75 | if [ -z "$mntp" ]; then # FIXME: this won't work for partitons mounted via UUID/LABEL
76 | grep -qw "root=/dev/$dev" /proc/cmdline && mntp="/"
77 | fi
78 | echo "$mntp"
79 |
80 | intr["$dev"]=${blkr["$dev"]}
81 | intw["$dev"]=${blkw["$dev"]}
82 | done
83 | echo
84 | done
85 | qexit < /dev/stdin &
86 | sleep $rr
87 | done
88 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Probably useful scripts
2 |
3 | ## [boost-tuner](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/boost-tuner)
4 | I'm not content that my Ryzen 3700 CPU boosts so often and so much while I'm simply browsing the web (governor: ondemand) which leads to the CPU operating at relatively high temperatures (45-60C, with an average run-of-the-mill air cooler and around 19C temperature in the room where I am) and voltages (1.5V voltage doesn't sound safe to me despite AMD assurance this voltage is 100% safe). The whole situation made me write the script which disables boost unless certain predefined processes are found to be running. At the moment I've allowed only compilation but you can add anything you want. To run it you may add it to `/etc/rc.d/rc.local`, e.g. this way:
5 | `( sleep 60 && exec /root/bin/boost-tuner & ) &` # this is not to delay any further commands and probably to disable boost only after the user has logged on (60 seconds are more than enough to enter your password). It must be run under root. As a user you may force enable boost by creating /tmp/boost, e.g. `touch /tmp/boost`
6 |
7 | ## [btc2usd](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/btc2usd)
8 | A shell script which shows the current bitcoin exchange rates for CoinDesk.com and CoinMarketCap.com in the United States dollars (USD). Requires the jq utility (can be installed in deb distros using `sudo apt-get install jq` or in Fedora using `sudo dnf install jq`). It could also be used under XFCE via the generic monitor applet.
9 |
10 | ## [clean_dnf](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/clean_dnf)
11 | Ever wanted to get rid of useless/unnecessary/redundant RPMs lying on your disk? There's a script for that which uses DNF. Run it without arguments to walk through all the installed packaged, or give it a single argument (I may fix it to accept any number of packages) to check whether other packages depend on it. It will skip the RPMs which result in the removal of protected packages like DNF itself.
12 |
13 | ## [defrag](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/defrag)
14 | fast recursive defrag for ext4
15 | * Fast recursive defragmentation for the specified directory residing on an ext4 filesystem; it doesn't support anything else
16 | * ext4 doesn't allow to defrag free space, so it's impossible to fully defragment a filesystem if free space is fragmented and there are files larger than continuous chunks of free space
17 | * If you want to defrag the root filesystem, start with `defrag /var`, then `defrag /` - files in var are usually heavily fragmented and need to be defragged first
18 | * It's theoretically possible to fully defrag the ext4 filesystem: you first need to shrink it, then expand it. I will never recommend this as it's quite a dangerous operation and mustn't be performed without backing up first; in addition you cannot obviously do that on a mounted filesystem
19 |
20 | ## [Firefox-updater](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/Firefox-updater)
21 | If you're unwilling to let your user account update Firefox (it's a little bit unsafe) I've created a script for this situation which could be added in a crontab. This script automatically updates the official Linux x86-64 Firefox distribution installed on your PC. It currently supports the ESR, stable and beta channels.
22 |
23 | ## [monitor_overclock](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/monitor_overclock)
24 | This script allows you to overclock your monitor (refresh rate/frequency) under Linux. It was created specifically for my sole monitor, so you'll have to adjust it if yours is different. Modifications to /etc/X11/xorg.conf.d/conf.conf are [required](https://devtalk.nvidia.com/default/topic/1054885/linux/monitor-refresh-frequency-overclocking-in-linux-is-not-available/).
25 |
26 | ## [watch_raw_io](https://raw.githubusercontent.com/birdie-github/useful-scripts/master/watch_raw_io)
27 | Monitor input/output read/write nicely formatted stats for all block devices/disks in the system in real time in Linux. Takes a single argument: a refresh interval which is 2 seconds by default. Works under a normal (non-root) user as well.
28 |
29 | ## Bottom line
30 |
31 | To be honest I have close to a hundred such scripts but many of them are either silly or useful only for me. I publish them elsewhere.
32 |
33 | ## SAST Tools
34 |
35 | [PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
36 |
--------------------------------------------------------------------------------
/Firefox-updater:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | #----------------------------------------------------------------------
4 | # Description: the official Linux x86-64 Firefox distribution updater
5 | # Allows you to update Firefox from command line/CLI/terminal
6 | #
7 | # Author: Artem S. Tashkinov
8 | # Created at: Wed Dec 20 13:50 2017
9 | # 2019-05-29 15:08:05 Firefox 68 fix
10 | # 2022-12-17 01:47:32 Firefox 108 fix
11 | # 2025-04-09 14:34:47 Firefox 137 fix for the locale variable
12 | # 2025-05-01 21:45:14 Firefox 138 fix for the updater tool
13 | # Computer: localhost.localdomain
14 | # System: Linux 4.14.3-300.fc27.x86_64 on x86_64
15 | #
16 | # Copyright (c) 2017-2025 Artem S. Tashkinov All rights reserved.
17 | #----------------------------------------------------------------------
18 |
19 | # Comment out to disable or set the number to add an iptables rule for the OUTPUT chain
20 | # firewall=6
21 |
22 | diewithmsg()
23 | {
24 | echo "ERROR: $1"
25 | exit 1
26 | }
27 |
28 | sanitychecker()
29 | {
30 | test -z "$1" && diewithmsg "Empty version string. Perhaps this script needs to be updated or wget failed."
31 | result=`echo "$1" | sed 's/\.//g;s/b//;s/[0-9]//g;s/esr//'`
32 | test -n "$result" && diewithmsg "The version string [$1] is wrong."
33 | }
34 |
35 | normalize()
36 | {
37 | # Add .0 for XX.0 releases, i.e. 60.0 becomes 60.0.0; remove esr; remove beta; awk: 60.1.2 -> 60 000 000 + 1000 + 2
38 | # will fail to upgrade beta to release
39 | echo "$1" | sed 's/^[0-9]*\.0$/\0.0/;s/esr//;s/b/\./' | awk -F "." '{print $1*1000000+$2*1000+$3}'
40 | }
41 |
42 | extractversion()
43 | {
44 | # Firefox 108+ fix
45 | datasrc=modules/AppConstants.jsm
46 | unzip -p "$1/omni.ja" "$datasrc" &> /dev/null || datasrc=modules/AppConstants.sys.mjs
47 | unzip -p "$1/omni.ja" "$datasrc" 2>/dev/null | egrep "MOZ_APP_VERSION_DISPLAY|MOZ_UPDATE_CHANNEL|INSTALL_LOCALE" | sed 's/: /=/;s/,//'
48 | # Firefox 68+ fix
49 | flocale=`unzip -p "$1/omni.ja" "$datasrc" 2>/dev/null | grep INSTALL_LOCALE`
50 | test -z "$flocale" && echo "INSTALL_LOCALE=`unzip -p "$1/omni.ja" update.locale 2>/dev/null`"
51 | # Firefox 137+ fix
52 | test -z "$flocale" && echo "INSTALL_LOCALE=`unzip -p "$1/omni.ja" default.locale 2>/dev/null`"
53 | }
54 |
55 | fetch()
56 | {
57 | wget --progress=dot:giga -O update.mar "$1"
58 | }
59 |
60 | unzip -v &> /dev/null || diewithmsg "Please install unzip first!"
61 | wget -V &> /dev/null || diewithmsg "Please install wget first!"
62 | bc -v &> /dev/null || diewithmsg "Please install bc first!"
63 |
64 | test -z "$2" && diewithmsg "Usage is: `basename "$0"` user_for_update firefox_installation_directory forced_new_version[optional]"
65 | grep -qw "$1" /etc/passwd || diewithmsg "Unable to find the user '$1'"
66 | test -f "$2/firefox" || diewithmsg "'$2' is not a valid Firefox installation directory"
67 |
68 | id=`id -u` || diewithmsg "id failed!"
69 |
70 | if [ "$id" == "0" ]; then
71 | if [ -n "$firewall" ]; then
72 | if [ -z "`iptables-save | grep uid-owner\ $1`" ]; then
73 | echo -n "Adding iptables rule number $firewall for the user $1 ... "
74 | iptables -I OUTPUT "$firewall" -p tcp -m owner --uid-owner "$1" -j ACCEPT && echo OK || diewithmsg "Failed"
75 | else
76 | echo "An iptables rule is already active"
77 | fi
78 | fi
79 |
80 | # Change files/directories ownership unconditionally
81 | echo -n "Applying permissions $1:$1 for '$2' ... "
82 | chown -Rc "$1:$1" "$2" && echo OK || diewithmsg "This mustn't happen."
83 |
84 | echo -n "Dropping root privileges ... "
85 | sudo -u "$1" "$0" "$1" "$2" "$3"
86 | exit $?
87 | fi
88 |
89 | echo "OK"
90 | echo "Running as user '`id -un`'."
91 |
92 | source <(extractversion "$2")
93 | versionhave="$MOZ_APP_VERSION_DISPLAY"
94 |
95 | echo "Locale: $INSTALL_LOCALE"
96 |
97 | if [ "$MOZ_UPDATE_CHANNEL" == "beta" ]; then
98 | echo "Selected the BETA update channel"
99 | channel="-beta"
100 | elif [ "$MOZ_UPDATE_CHANNEL" == "esr" ]; then
101 | echo "Selected the ESR update channel"
102 | channel="-esr"
103 | versionhave="${versionhave}esr"
104 | else
105 | echo "Selected the STABLE update channel"
106 | channel=""
107 | fi
108 |
109 | echo -n "Installed version: "
110 | sanitychecker "$versionhave"
111 | echo "$versionhave"
112 |
113 | echo -n "Available version: "
114 | versionnext="$3"
115 | if [ -z "$3" ]; then
116 | versionnext=`wget --spider --timeout=5 "https://download.mozilla.org/?product=firefox$channel-latest&os=linux64&lang=$INSTALL_LOCALE" 2>&1 | grep Location | sed 's/.*releases\/\(.*\)\/linux.*/\1/'`
117 | fi
118 | sanitychecker "$versionnext"
119 | echo "$versionnext"
120 |
121 | test "$versionhave" == "$versionnext" && echo "You're up to date. Exiting." && exit 0
122 |
123 | norm_vershave=`normalize "$versionhave"`
124 | norm_versnext=`normalize "$versionnext"`
125 | # bc: the result of all boolean operations are 0 and 1 (for false and true) as in relational expressions.
126 | test "`echo "$norm_vershave > $norm_versnext" | bc -l`" -eq 1 && diewithmsg "The installed version is newer than the available update! Perhaps you've updated manually."
127 |
128 | workdir=`mktemp -d`
129 | test -d "$workdir" || diewithmsg "Unable to create a working directory"
130 | cd "$workdir" || diewithmsg "Unable to enter a working directory"
131 |
132 | url_base="https://ftp.mozilla.org/pub/firefox/releases/$versionnext/update/linux-x86_64/$INSTALL_LOCALE"
133 | upd_incr="firefox-$versionhave-$versionnext.partial.mar"
134 | upd_full="firefox-$versionnext.complete.mar"
135 | url_incr="$url_base/$upd_incr"
136 | url_full="$url_base/$upd_full"
137 | echo -e "\nTrying to download delta from $versionhave to $versionnext ... "
138 | if ! fetch "$url_incr"; then
139 | echo "Trying to download full update to $versionnext ... "
140 | fetch "$url_full" || diewithmsg "Cannot download neither update"
141 | fi
142 |
143 | echo "Applying update ..."
144 | if [ $norm_vershave -lt 138000000 ]; then
145 | LD_LIBRARY_PATH="$2" "$2/updater" "$workdir" "$2" "$2"
146 | else
147 | LD_LIBRARY_PATH="$2" "$2/updater" 3 "$workdir" "$2" "$2" first 0
148 | fi
149 |
150 | echo -n "Cleaning up ... "
151 | rm -rf "$workdir" && echo OK
152 |
153 | echo "All done"
154 |
--------------------------------------------------------------------------------
/oneliners:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 |
3 | # Shows the maximum frequency of all CPU cores in the system
4 | cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq | awk '{if (max<$0) max=$0}END{printf "%.2fGHz",max/1000000}'
5 |
6 | # Maximum p7zip compression
7 | # mmt4 + 1.5GB dictionary requires 32GB of free(!) RAM
8 | 7za a -mx=9 -myx=9 -mfb=273 -bt -slp -mmt4 -md=1536m -mqs archive_name.7z [files or/and directories]
9 |
10 | # Maximum 7zz compression - requires a ton of RAM - 44GB maybe
11 | 7zz a -mx=9 -myx=9 -mfb=273 -bt -slp -mmt2 -md=3840m -mqs -stl -snl archive_name.7z [files or/and directories]
12 |
13 | # Compute md5sum for all the files and subdirectoties' files in the current directory
14 | find . -type f -print0 | xargs -0 [nocache] md5sum > output-file.md5
15 |
16 | # My favourite rsync invocation (mind the trailing slashes!)
17 | [ionice -c3] [nocache] rsync -avc --progress --preallocate --delete source/ /destination
18 |
19 | # My favourite mkfs.ext4/mke2fs invocation
20 | mkfs.ext4 -O ^huge_file,^has_journal /dev/device
21 |
22 | # My favourite pv invocation
23 | [nocache] pv -petrab /dev/source | tee /dev/destination | md5sum
24 |
25 | # Too tired of remembering all the flags required to launch Google Chrome, originally 2018-03-06 22:47:33
26 | env TZ=USA/Pacific FREETYPE_PROPERTIES=truetype:interpreter-version=35 chrome --disk-cache-dir=/tmp/.chrome-cache "$@"
27 |
28 | # Extract a cpio archive
29 | cpio -i -m -d -F "name.cpio"
30 |
31 | # Create a cpio archive
32 | find "directory" -depth | cpio -o -H crc -O "name.cpio"
33 |
34 | # Poll/watch NVIDIA GPU temperature and power consumption (with proprietary NVIDIA drivers) - e.g. for XFCE generic monitor
35 | nvidia-smi --query-gpu=temperature.gpu,power.draw --format=csv,noheader,nounits
36 |
37 | # Compile VMWare kernel modules, 2009-08-30 05:35:03
38 | vmware-modconfig --console --install-all
39 |
40 | # online ext4 defrag for the current directory and all subdirs - no idea when I wrote it, around 2017 maybe?
41 | # check the defrag script instead - works many times faster
42 | find . -xdev -type f | while read filename; do filefrag "$filename" | egrep -v ": 1 extent|: 0 extents"; done | while read line; do fname=`echo "$line" | awk -F ': ' '{print $1}'`; e4defrag -v "$fname" | grep -v "e4defrag 1"; done
43 |
44 | # dmesg show only serious messages without timestamps
45 | dmesg -t --level=alert,crit,err,warn
46 |
47 | # Build VirtualBox/NVIDIA modules with a custom kernel version
48 | KERN_VER=$VERSION make -j16 # VirtualBox
49 | SYSSRC=/lib/modules/$VERSION/source make -j16 # NVIDIA
50 |
51 | # Not one-liners
52 | # image2vvc - convert a bmp image to VVC somewhat losslessly (rgb to yuv420p10le is not lossless), 2021-04-27 19:39:38
53 | for i in "$@"; do
54 | xy=`identify "$i" | sed 's/.* \([0-9]*x[0-9]*\).*/\1/'` # ([[:digit:]]{1,5}x[[:digit:]]{1,5})
55 | base="${i%.*}"
56 | test -f "$base.vvc" && echo "Found $base.vvc, skipping" && continue
57 | echo "File: $i Dimensions: $xy"
58 | ffmpeg -hide_banner -loglevel error -nostats -s "$xy" -i "$i" -vframes 1 -pix_fmt yuv420p10le "$base.yuv"
59 | # ffmpeg -s "$xy" -pix_fmt yuv420p10le -i "$base.yuv" -vframes 1 "$base.bmp"
60 | # --verbosity 5
61 | nice -20 ./vvencapp --input "$base.yuv" --size "$xy" --format yuv420_10 --frames 1 --preset slower --qp 0 --output "$base.vvc"
62 | rm "$base.yuv"
63 | touch -r "$i" "$base.vvc"
64 | done
65 |
66 | # vvc2bmp - decompress VVC to bmp, 2021-04-27 20:21:06
67 | for i in "$@"; do
68 | base="${i%.*}"
69 | test -f "$base.bmp" && echo "Found $base.bmp, skipping" && continue
70 | res=`./vvdecapp --bitstream "$i" --output "$base.yuv" | awk '/SizeInfo:/{print $4}'`
71 | echo "File: $i Dimensions: $res"
72 | ffmpeg -hide_banner -loglevel error -nostats -pix_fmt yuv420p10le -s "$res" -i "$base.yuv" "$base.bmp"
73 | rm "$base.yuv"
74 | done
75 |
76 | # get current date/time in a sane format, e.g.
77 | LANG=en_DK.UTF-8 date +"%x %X"
78 | 2022-08-09 02:54:42
79 |
80 | # Set git repo files modification dates/times to the time they were commited (stolen somewhere - don't remember where)
81 | git ls-tree -r --name-only HEAD | while read filename; do unixtime=$(git log -1 --format="%at" -- "${filename}"); touchtime=$(date -d @$unixtime +'%Y%m%d%H%M.%S'); touch -t ${touchtime} "${filename}"; done
82 |
83 | # What I run daily after the Sun sets:
84 | xcalib -v -blue 0.9 0 90 -alter
85 |
86 | # This is to cancel it
87 | xcalib -c
88 |
89 | # Main PC Fedora 38 nodocs packages (too fat)
90 | # 2024-01-01 16:17:36 UTC
91 | # alsa-lib annobin-docs annobin-plugin-gcc atk atk-devel bison cups-devel exo flac-devel fontconfig freeglut-devel freetype-devel gcc gdk-pixbuf2 gdk-pixbuf2-devel git git-core-doc glib2 glibc gnutls gnutls-devel goldendict lame-libs lcms2-devel libaom-devel libcurl libcurl-devel libgpg-error libicu-devel libidn2 libtiff libtiff-devel libtool libvpx-devel libxml2 libxml2-devel libxslt-devel libxslt-devel.i686 libxslt-devel.x86_64 mpfr mpg123-devel ncurses nettle-devel nspr nss openssl-devel opus-devel pango-devel pcre2-devel pcre-devel pipewire pulseaudio-libs-devel source-highlight Thunar valgrind xfce4-panel xfce4-settings xfce4-terminal xfce4-weather-plugin xfdesktop zlib-devel
92 | eatmydata dnf update --setopt='tsflags=nodocs' `cat ~/NODOCS`
93 |
94 | # Elite laptop
95 | # 2023-12-15 00:08:08
96 | # libaom-devel alsa-lib-devel annobin-docs bind-libs bison cups-devel curl expat fftw-libs-single flac-devel fontconfig freetype-devel gcc gdk-pixbuf2 gdk-pixbuf2-devel git git-core-doc glib2 glibc gnutls gnutls-devel gparted hwloc-libs krb5-devel lame-libs libcurl libgpg-error libtiff-devel libtool libtraceevent libxml2 libxslt libxslt-devel libXt libXt-devel luajit mpg123-devel ncurses nettle-devel nspr nss openssl opus-devel pcre2-devel pcre-devel pulseaudio-libs-devel ristretto SDL2 source-highlight Thunar xfce4-panel xfce4-settings xfce4-terminal xfdesktop zlib-devel
97 |
98 | # Common list
99 | # alsa-lib alsa-lib-devel annobin-docs annobin-plugin-gcc atk atk-devel bind-libs bison cups-devel curl exo expat fftw-libs-single flac-devel fontconfig freeglut-devel freetype-devel gcc gcc-c++ gdk-pixbuf2 gdk-pixbuf2-devel git git-core-doc glib2 glibc gnutls gnutls-devel goldendict gparted hwloc-libs json-glib-devel krb5-devel lame-libs lcms2-devel libaom-devel libcurl libcurl-devel libgpg-error libicu-devel libidn2 libstdc++-devel libtiff libtiff-devel libthai-devel libtool libtraceevent libvpx-devel libxml2 libxml2-devel libxslt libxslt-devel libxslt-devel.i686 libxslt-devel.x86_64 libXt libXt-devel luajit mpfr mpg123-devel ncurses nettle-devel nspr nss openssl openssl-devel opus-devel pango-devel pcre2-devel pcre-devel pipewire pulseaudio-libs-devel ristretto SDL2 source-highlight Thunar valgrind xfce4-panel xfce4-settings xfce4-terminal xfce4-weather-plugin xfdesktop zlib-devel libnotify-devel libgit2-devel libao-devel libdatrie-devel cryptsetup eb-devel gtest-devel libsndfile-devel nvme-cli openvpn python3-setuptools wireguard-tools xorg-x11-proto-devel
100 |
--------------------------------------------------------------------------------
/cpumode:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #----------------------------------------------------------------------
3 | # Description: Put CPU to and backto full powersave mode
4 | # Author: Artem S. Tashkinov
5 | # Created at: Sun Jun 19 00:14:40 2005
6 | # System: Linux 2.6.12-k8 on i686
7 | #
8 | # Thu Jul 1 19:18:10 2010
9 | # 1.0 Added SMP support
10 | #
11 | # Sun Dec 25 16:50:36 2011
12 | # 1.1 Completely reworked modules loading mechanism; userspace governor
13 | #
14 | # Sun Jul 22 05:30:16 2012
15 | # 1.2 Set/Reset; Improved output
16 | #
17 | # Mon Aug 6 00:46:25 2012
18 | # 1.2.1 unified userspace and maxfreq; unified everything; more error checkings
19 | #
20 | # Mon Feb 11 06:19:04 2013
21 | # 1.2.2 --now shows frequency in MHz
22 | #
23 | # Fri May 3 00:41:13 2013
24 | # 1.2.3 I've discovered that after suspend/resume max CPU cores 2, 3 and 4
25 | # frequences have been reset. This script "thought" those numbers are always
26 | # the same, now we tread carefully and check if they are indeed different
27 | # Also fix userspace set frequency - maxfreq also has to be adjusted
28 | #
29 | # Sun Aug 6 03:01:58 2017
30 | # 1.2.4 add an option to show available frequencies
31 | #
32 | # Mon 14 Oct 2019 04:21:23 PM
33 | # 1.2.5 add boost support
34 | # truncate the resulting frequencies
35 | #
36 | # Wed Mar 23 09:35:13 PM 2022
37 | # 1.2.6 add schedutil governor support
38 | # show the CPU frequency driver
39 | #
40 | # Tue Apr 5 06:18:12 AM 2022
41 | # 1.2.7 fix setting [max] CPU frequency with amd-pstate
42 | #
43 | # Fri Jun 17 01:04:33 PM 2022
44 | # 1.2.8 multiple fixes for intel-pstate
45 | #
46 | # Thu Jun 23 07:38:05 AM 2022
47 | # 1.2.9 multiple optimizations and fixes for intel-pstate boost
48 | #
49 | # Copyright (c) 2005-2022 Artem S. Tashkinov. All rights reserved.
50 | #----------------------------------------------------------------------
51 |
52 | #set -x
53 | #PS4='+ $(date "+%s.%N ($LINENO) ")'
54 |
55 | version=1.2.9
56 | DRIVER=acpi_cpufreq
57 | lead=/sys/devices/system/cpu
58 |
59 | cpu0=$lead/cpu0/cpufreq
60 | min0=`cat $cpu0/cpuinfo_min_freq 2>/dev/null`
61 | max0=`cat $cpu0/cpuinfo_max_freq 2>/dev/null`
62 | lim0=`cat $cpu0/scaling_max_freq 2>/dev/null`
63 | favailfreq=$cpu0/scaling_available_frequencies
64 | SG=$lead/cpu*/cpufreq/scaling_governor
65 | SD=`cat $cpu0/scaling_driver 2>/dev/null`
66 |
67 | hz2mhz()
68 | {
69 | echo "$@" | awk 'END{ORS=" "; for (i=1;i<=NF;i++) print int($i/1000)"MHz"}' | sed 's/.$//'
70 | }
71 |
72 | setgov()
73 | {
74 | echo "$1" | tee $SG &> /dev/null
75 | test -n "`cat $SG | grep -v "$1"`" && echo "Failed swithing to the '$1' governor! Might not be supported or compiled in." && exit 100
76 | echo "$1 mode/governor has been activated"
77 | }
78 |
79 | getgov()
80 | {
81 | echo "Governor: `cat $SG | sort | uniq`"
82 | }
83 |
84 | boost()
85 | {
86 | on=1
87 | off=0
88 | fboost=/sys/devices/system/cpu/cpufreq/boost
89 | test "$SD" = "intel_pstate" && fboost=/sys/devices/system/cpu/intel_pstate/no_turbo && on=0 && off=1
90 |
91 | echo -n "Boost: "
92 | test ! -f "$fboost" && echo "is not supported" && return
93 | if [ -z "$1" ]; then
94 | test "`cat $fboost`" == "$on" && echo "enabled" || echo "disabled"
95 | else
96 | arg=`echo "$1" | tr A-Z a-z`
97 | if [ "$arg" == "1" -o "$arg" == "on" -o "$arg" == "yes" ]; then
98 | echo "$on" > $fboost
99 | test "`cat $fboost`" == "$on" && echo "enabled" || echo "FAILED to enable"
100 | else
101 | echo "$off" > $fboost
102 | test "`cat $fboost`" == "$off" && echo "disabled" || echo "FAILED to disable"
103 | fi
104 | fi
105 | }
106 |
107 | availfreq()
108 | {
109 | echo -n "The available frequencies are: "
110 | if [ -f "$favailfreq" ]; then
111 | cat "$favailfreq"
112 | else
113 | echo "anything from $(hz2mhz $min0) ($min0 KHz) to $(hz2mhz $max0)($max0 KHz)"
114 | fi
115 | }
116 |
117 | setspeed()
118 | {
119 | allowed=`cat $favailfreq 2>/dev/null`
120 | if [ -z "$2" ]; then
121 | echo -n "Need an argument to continue. "
122 | availfreq
123 | return 1
124 | fi
125 |
126 | if [ -z "`echo "$allowed" | grep -w "$2"`" -a -f $favailfreq ]; then
127 | echo "'$2' is not amongst the allowed frequencies: "
128 | echo $allowed
129 | return 2
130 | fi
131 |
132 | if [ "$1" == "userspace" ]; then
133 | setgov userspace
134 | echo -n "Setting user defined frequency of `hz2mhz $2` ... "
135 | echo "$2" | tee $lead/cpu*/cpufreq/scaling_max_freq > /dev/null
136 | echo "$2" | tee $lead/cpu*/cpufreq/scaling_setspeed > /dev/null
137 | [ "`cat $cpu0/scaling_setspeed 2>/dev/null`" == "$2" ] && echo OK || echo Failed
138 | elif [ "$1" == "maxfreq" ]; then
139 | getgov
140 | echo "Setting maximum frequency of `hz2mhz $2`"
141 | echo "$2" | tee $lead/cpu*/cpufreq/scaling_max_freq > /dev/null
142 | echo "Result: $(cat $lead/cpu*/cpufreq/scaling_max_freq | awk '{print $1/1000"MHz"}' | uniq)"
143 | else
144 | echo "Don't know what to do"
145 | fi
146 | }
147 |
148 | if [ ! -n "$SD" -a "$1" != "-l" -a "$1" != "--load" ]; then
149 | echo "Error: cpufrequency subsystem is not running!"
150 | exit 1
151 | fi
152 |
153 | governors=`cat $cpu0/scaling_available_governors 2>&1`
154 |
155 | for i in performance conservative userspace powersave ondemand; do
156 | cgovern=`echo $governors | grep $i`
157 | if [ -z "$cgovern" ]; then
158 | #echo "CPU governor '$i' is not present" # intel_pstate doesn't work with conservative userspace ondemand - no idea why - let's silence everything
159 | module="cpufreq_$i"
160 | #echo "Installing $module ... " # likewise
161 | modprobe $module >/dev/null 2>&1
162 | if [ "$?" != "0" ]; then
163 | echo "Error: can't load $module module. Do you have it installed?"
164 | exit 1
165 | else
166 | #echo "OK"
167 | :
168 | fi
169 | fi
170 | done
171 |
172 | test "$1" = "-l" -o "$1" = "--load" || current=`cat $SG`
173 |
174 | case $1 in
175 | --version|-v) #print the version number
176 | echo "`basename $0` version "$version
177 | exit 0;;
178 |
179 | --help|-h) #Display the help screen
180 | echo "Usage: `basename $0` [OPTION]"
181 | echo " -M --maximum - performance mode (minimum powersavings)"
182 | echo " -m --mimimum - powersave mode (minimum performance)"
183 | echo " -a --auto - ondemand mode (rapid auto performance)"
184 | echo " -b --boost - enable/disable boost"
185 | echo " -c --conservative - conservative mode (gradual auto performance)"
186 | echo " -s --schedutil - schedutil governor"
187 | echo " -u --userspace FREQ - user defined CPU frequency"
188 | echo " -l --load - load CPU powersave driver"
189 | echo " -n --now - show current mode"
190 | echo " -S --set FREQ - set maximum CPU frequency"
191 | echo " -r --reset - reset maximum CPU frequency"
192 | echo " -f --frequencies - show available frequencies"
193 | echo " -t --time - percentage of time spent in states"
194 | echo " -v --version - show application version and exit"
195 | echo " -h --help - show this help and exit"
196 | exit 0;;
197 |
198 | --load|-l)
199 | KDRIVER=`echo $DRIVER | sed 's/-/_/g'`
200 | if [ -z "`lsmod | grep $KDRIVER`" ]; then
201 | modprobe $DRIVER && echo "CPU powersave driver has been loaded successfully"
202 | else
203 | echo "CPU powersave driver is already loaded"
204 | fi
205 | exit;;
206 |
207 | --userspace|-u)
208 | setspeed userspace "$2"
209 | exit;;
210 |
211 | --conservative|-c)
212 | setgov conservative
213 | exit;;
214 |
215 | --schedutil|-s)
216 | setgov schedutil
217 | exit;;
218 |
219 | --minimum|-m)
220 | setgov powersave
221 | exit;;
222 |
223 | --maximum|-M)
224 | setgov performance
225 | exit;;
226 |
227 | --set|-S)
228 | setspeed maxfreq "$2"
229 | exit;;
230 |
231 | --reset|-r)
232 | getgov
233 | echo "Resetting maximum frequency ... "
234 | echo $max0 | tee $lead/cpu*/cpufreq/scaling_max_freq > /dev/null
235 | cat $lead/cpu*/cpufreq/scaling_max_freq
236 | exit;;
237 |
238 | --auto|-a)
239 | setgov ondemand
240 | exit;;
241 |
242 | --boost|-b)
243 | boost "$2"
244 | exit;;
245 |
246 | --frequencies|-f)
247 | availfreq
248 | exit;;
249 |
250 | --time|-t)
251 | if [ -f /sys/devices/system/cpu/cpufreq/policy0/stats/time_in_state ]; then
252 | awk '{time[$1]+=$2;sum+=$2}END{for (var in time) print var,":",time[var]/sum*100,"%"}' /sys/devices/system/cpu/cpufreq/policy*/stats/time_in_state
253 | else
254 | echo "time_in_state is not supported"
255 | fi
256 | exit;;
257 |
258 | --now|-n|*)
259 | echo "Driver: $SD"
260 | boost
261 | getgov
262 |
263 | cores=`cat $lead/cpu*/cpufreq/scaling_cur_freq 2>/dev/null`
264 | if [ -n "$cores" ]; then
265 | echo -n "Average and maximum frequencies now: "
266 | hz2mhz `echo $cores | awk 'BEGIN{max=0;avr=0} {for (i=1;i<=NF;i++) {if ($i>max) max=$i; avr+=$i}} END{print avr/NF, max}'`
267 | echo
268 |
269 | #echo -n "Cores are at: " # no one cares considering there are PCs with 32 virtual cores
270 | #hz2mhz $cores
271 | #echo
272 | else
273 | echo "This should have never happened!"
274 | fi
275 |
276 | # Check if maximum CPU frequences are mixed or maxed out
277 | for i in `cat $lead/cpu*/cpufreq/scaling_max_freq`; do
278 | this=$i
279 | test "$max0" != "$i" && notmaxed=1
280 | test "$next" != "$this" -a -n "$vin" && nonuni=1 # vin - do not run on a first loop invocation
281 | next=$this
282 | vin=1
283 | done
284 |
285 | if [ -z "$notmaxed" ]; then
286 | echo "No frequency limit is enforced."
287 | else
288 | if [ -n "$nonuni" ]; then
289 | echo "Maximum CPU frequency per CPU is :"
290 | hz2mhz `cat $lead/cpu*/cpufreq/scaling_max_freq`
291 | echo
292 | else
293 | echo "Maximum allowed frequency is `hz2mhz $lim0` (out of `hz2mhz $max0`)"
294 | fi
295 | fi
296 |
297 | exit;;
298 | esac
299 |
--------------------------------------------------------------------------------
/watchrawio.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | """
4 | Fast, low-flicker block I/O viewer (Python + curses, stdlib only)
5 |
6 | Original Bash script:
7 | Copyright (C) 2013-2025 Artem S. Tashkinov
8 | Licensed under the GNU General Public License v2.0 or later (GPL-2.0+)
9 |
10 | Python port and enhancements:
11 | Portions © 2025 ChatGPT5 (OpenAI) — contributed under GPL-2.0+,
12 | permission granted to relicense with the original work.
13 |
14 | License:
15 | This program is free software: you can redistribute it and/or modify
16 | it under the terms of the GNU General Public License as published by
17 | the Free Software Foundation, either version 2 of the License, or
18 | (at your option) any later version.
19 |
20 | This program is distributed in the hope that it will be useful,
21 | but WITHOUT ANY WARRANTY; without even the implied warranty of
22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 | GNU General Public License for more details.
24 |
25 | You should have received a copy of the GNU General Public License
26 | along with this program. If not, see .
27 |
28 | Description:
29 | Interactive, real-time block I/O monitor without flicker.
30 | - Natural device sort and partition indenting
31 | - %util from /proc/diskstats
32 | - Toggle hide zeros ('0')
33 | - Toggle show active-only ('A')
34 | - Instant key response
35 | - Locale-aware or custom thousands separators
36 | - Device prefix filtering (default: physical block devices)
37 | """
38 |
39 | import argparse
40 | import curses
41 | import locale
42 | import os
43 | import re
44 | import sys
45 | import time
46 | from collections import defaultdict
47 |
48 | DEFAULT_PREFIXES = ["sd", "nvme", "md", "vd", "dm-", "mmcblk"]
49 | DISKSTATS = "/proc/diskstats"
50 | MOUNTS = "/proc/mounts"
51 | SYS_BLOCK = "/sys/block"
52 |
53 | # Field indices in /proc/diskstats (Linux kernel docs):
54 | # 0 major, 1 minor, 2 name, 3 reads_completed, 4 reads_merged,
55 | # 5 sectors_read, 6 time_reading_ms, 7 writes_completed, 8 writes_merged,
56 | # 9 sectors_written, 10 time_writing_ms, ...
57 | IDX_NAME = 2
58 | IDX_SEC_READ = 5
59 | IDX_SEC_WRIT = 9
60 |
61 | NVME_PART_RE = re.compile(r"^(nvme.+?)p\d+$")
62 | DIGIT_GROUP_RE = re.compile(r"(\d)(?=(\d{3})+(?!\d))")
63 |
64 | hide_zeros = False
65 | show_active_only = False
66 |
67 | def is_partition(name: str) -> bool:
68 | return base_of(name) != name
69 |
70 | # Put these near the other regex/constants
71 | _NAT_PATTERNS = [
72 | re.compile(r'^(nvme\d+n\d+)(?:p(\d+))?$'),
73 | re.compile(r'^(mmcblk\d+)(?:p(\d+))?$'),
74 | re.compile(r'^(loop\d+)(?:p(\d+))?$'),
75 | re.compile(r'^((?:sd|vd|xvd|zd)[a-z]+)(\d+)?$'),
76 | re.compile(r'^(md\d+)(?:p(\d+))?$'),
77 | re.compile(r'^(dm-\d+)(?:p(\d+))?$'),
78 | ]
79 |
80 | def natural_key(devname: str) -> tuple:
81 | """Sort base devices first, then their partitions numerically."""
82 | for pat in _NAT_PATTERNS:
83 | m = pat.match(devname)
84 | if m:
85 | base, p = m.group(1), m.group(2)
86 | if p is None:
87 | return (base, 0, 0)
88 | return (base, 1, int(p))
89 | # Fallback: keep as-is
90 | return (devname, 0, 0)
91 |
92 | def base_of(dev: str) -> str:
93 | """Return the base device for lbsize lookups (nvme0n1p2 -> nvme0n1, sda1 -> sda, etc.)."""
94 | for pat in _NAT_PATTERNS:
95 | m = pat.match(dev)
96 | if m:
97 | return m.group(1)
98 | return dev # unknown pattern: treat as base
99 |
100 | def is_partition(name: str) -> bool:
101 | """Classify partitions precisely across device families."""
102 | if re.match(r'^nvme\d+n\d+p\d+$', name): return True
103 | if re.match(r'^mmcblk\d+p\d+$', name): return True
104 | if re.match(r'^loop\d+p\d+$', name): return True
105 | if re.match(r'^((?:sd|vd|xvd|zd)[a-z]+)\d+$', name): return True
106 | if re.match(r'^(md\d+|dm-\d+)p\d+$', name): return True
107 | return False
108 |
109 | def parse_args():
110 | ap = argparse.ArgumentParser(description="Watch block I/O per device without flicker.")
111 | ap.add_argument("-r", "--refresh", type=float, default=2.0, help="Refresh rate (seconds). Default: 2.0")
112 | ap.add_argument("--locale", action="store_true", help="Use system locale for digit grouping.")
113 | ap.add_argument("--sep", type=str, default=None, help="Hardcode thousands separator (e.g., ',', '.', ' ').")
114 | ap.add_argument("--devices", nargs="*", default=DEFAULT_PREFIXES,
115 | help=f"Device name prefixes to include. Default: {', '.join(DEFAULT_PREFIXES)}")
116 | return ap.parse_args()
117 |
118 |
119 | def set_locale(use_locale: bool):
120 | if not use_locale:
121 | return
122 | try:
123 | # '' => use environment (LC_ALL/LC_NUMERIC/LANG)
124 | locale.setlocale(locale.LC_ALL, "")
125 | except locale.Error:
126 | # Fall back silently; grouping will still work with custom sep or commas.
127 | pass
128 |
129 |
130 | def group_num(n: int, use_locale: bool, sep: str | None) -> str:
131 | """Format integer with thousands grouping."""
132 | if sep is not None:
133 | # Hardcoded separator (no locale): insert every 3 digits
134 | s = str(n)
135 | return DIGIT_GROUP_RE.sub(rf"\1{sep}", s)
136 | if use_locale:
137 | # Locale-aware grouping
138 | try:
139 | return locale.format_string("%d", n, grouping=True)
140 | except Exception:
141 | pass
142 | # Default: comma
143 | return f"{n:,}"
144 |
145 |
146 | def base_of(dev: str) -> str:
147 | # sdX1 -> sdX, vdX1 -> vdX, dm-0 -> dm-0, nvme0n1p2 -> nvme0n1
148 | m = NVME_PART_RE.match(dev)
149 | if m:
150 | return m.group(1)
151 | # strip trailing digits for typical devices (sdX1, vdX1, xvdX1, etc.)
152 | i = len(dev) - 1
153 | while i >= 0 and dev[i].isdigit():
154 | i -= 1
155 | return dev if i == len(dev) - 1 else dev[:i + 1]
156 |
157 |
158 | def load_lb_sizes(prefixes: list[str]) -> dict[str, int]:
159 | """Read logical block size per base device from /sys/block."""
160 | sizes: dict[str, int] = {}
161 | try:
162 | for name in os.listdir(SYS_BLOCK):
163 | if not any(name.startswith(p) for p in prefixes):
164 | continue
165 | p = os.path.join(SYS_BLOCK, name, "queue", "logical_block_size")
166 | try:
167 | with open(p, "rt") as f:
168 | sizes[name] = int(f.read().strip())
169 | except Exception:
170 | sizes[name] = 512
171 | except FileNotFoundError:
172 | pass
173 | return sizes
174 |
175 |
176 | def resolve_mounts() -> dict[str, str]:
177 | """Map device name (e.g., sda1, nvme0n1p2, dm-0) to mountpoint."""
178 | byname: dict[str, str] = {}
179 | try:
180 | with open(MOUNTS, "rt") as f:
181 | for line in f:
182 | parts = line.split()
183 | if len(parts) < 2:
184 | continue
185 | src, mnt = parts[0], parts[1]
186 | if src.startswith("/dev/"):
187 | name = os.path.basename(src)
188 | byname[name] = mnt
189 | elif src.startswith("/dev/disk/"):
190 | # resolve symlink if possible
191 | try:
192 | real = os.path.realpath(src)
193 | if real.startswith("/dev/"):
194 | name = os.path.basename(real)
195 | byname[name] = mnt
196 | # also record the mapper name, e.g., /dev/mapper/vg-lv
197 | if real.startswith("/dev/mapper/"):
198 | mapper = os.path.basename(real)
199 | byname[mapper] = mnt
200 | except Exception:
201 | pass
202 | except FileNotFoundError:
203 | pass
204 |
205 | # kernel cmdline root fallback
206 | try:
207 | with open("/proc/cmdline", "rt") as f:
208 | cmd = f.read()
209 | m = re.search(r"root=/dev/(\S+)", cmd)
210 | if m:
211 | byname.setdefault(m.group(1), "/")
212 | except Exception:
213 | pass
214 | return byname
215 |
216 |
217 | def read_diskstats() -> list[tuple[str, int, int, int]]:
218 | """Return list of (name, sectors_read, sectors_written, busy_ms)."""
219 | out: list[tuple[str, int, int, int]] = []
220 | try:
221 | with open(DISKSTATS, "rt") as f:
222 | for line in f:
223 | parts = line.split()
224 | if len(parts) < 13: # sanity check
225 | continue
226 | name = parts[IDX_NAME]
227 | try:
228 | sr = int(parts[IDX_SEC_READ])
229 | sw = int(parts[IDX_SEC_WRIT])
230 | busy_ms = int(parts[12]) # time spent doing I/Os
231 | except ValueError:
232 | continue
233 | out.append((name, sr, sw, busy_ms))
234 | except FileNotFoundError:
235 | pass
236 | return out
237 |
238 |
239 | def should_keep(name: str, prefixes: list[str]) -> bool:
240 | return any(name.startswith(p) for p in prefixes)
241 |
242 |
243 | def draw_header(stdscr, rr: float):
244 | stdscr.addstr(0, 0, time.strftime("Date: %a %b %d %T %Z"))
245 | stdscr.addstr(0, 35, f"[ refresh rate: {rr:g} s ]")
246 | stdscr.addstr(
247 | 1, 0,
248 | "{:<14} {:>17} {:>17} {:>17} {:>17} {:>8} {}".format(
249 | "Device", "Read bytes", "Written bytes", "Interval read", "Interval written", "%util", "Mount point"
250 | )
251 | )
252 |
253 | def fmt_val(val):
254 | if hide_zeros and val == 0:
255 | return ""
256 | return group_num(val, args.locale, args.sep)
257 |
258 | def fmt_util(val):
259 | if hide_zeros and abs(val) < 0.0001:
260 | return ""
261 | return f"{val:.1f}" # one decimal, no float garbage
262 |
263 | def main(stdscr, args):
264 | import curses, time
265 | from collections import defaultdict
266 |
267 | # We assign to these, so declare globals (you said you made them global).
268 | global hide_zeros, show_active_only
269 | try:
270 | hide_zeros
271 | except NameError:
272 | hide_zeros = False
273 | try:
274 | show_active_only
275 | except NameError:
276 | show_active_only = False
277 |
278 | # Curses setup
279 | curses.curs_set(0)
280 | stdscr.nodelay(True)
281 |
282 | # Caches & state
283 | lbsize = load_lb_sizes(args.devices)
284 | mounts = resolve_mounts()
285 | mount_refresh_every = 20
286 | iter_no = 0
287 |
288 | prev_r = defaultdict(int)
289 | prev_w = defaultdict(int)
290 | prev_busy = defaultdict(int)
291 | primed = set() # devices we’ve initialized
292 |
293 | ROW0 = 2 # first row after header
294 |
295 | while True:
296 | iter_no += 1
297 | if iter_no % mount_refresh_every == 1:
298 | mounts = resolve_mounts()
299 |
300 | # Read all stats once, filter, and natural-sort
301 | stats = read_diskstats() # [(name, sr, sw, busy_ms), ...]
302 | stats = [t for t in stats if should_keep(t[0], args.devices)]
303 | stats.sort(key=lambda t: natural_key(t[0]))
304 |
305 | # Build rows with activity flags; remember if any partition of a base was active
306 | rows_buf = [] # (name, base, is_part, active, line)
307 | base_has_active = {} # base_name -> True if any child active this tick
308 |
309 | for (name, sr, sw, busy_ms) in stats:
310 | base = base_of(name)
311 | is_part = is_partition(name)
312 | lbs = lbsize.get(base, 512)
313 |
314 | dr = max(0, sr - prev_r[name])
315 | dw = max(0, sw - prev_w[name])
316 | db = max(0, busy_ms - prev_busy[name])
317 |
318 | prev_r[name] = sr
319 | prev_w[name] = sw
320 | prev_busy[name] = busy_ms
321 |
322 | total_r = sr * lbs
323 | total_w = sw * lbs
324 | int_r = dr * lbs
325 | int_w = dw * lbs
326 |
327 | # util% over the nominal refresh (Option A)
328 | util = (db / (args.refresh * 1000.0)) * 100.0
329 | if util > 100.0:
330 | util = 100.0
331 | util_display = util if not is_part else 0.0
332 |
333 | mnt = mounts.get(name, "")
334 | display_name = (" " + name) if is_part else name
335 |
336 | # NOTE: fmt_val / fmt_util return STRINGS; use {:>..} (no .1f here)
337 | line = "{:<14} {:>17} {:>17} {:>17} {:>17} {:>8} {}".format(
338 | display_name,
339 | fmt_val(total_r),
340 | fmt_val(total_w),
341 | fmt_val(int_r),
342 | fmt_val(int_w),
343 | fmt_util(util_display),
344 | mnt
345 | )
346 |
347 | active = (int_r != 0) or (int_w != 0)
348 | rows_buf.append((name, base, is_part, active, line))
349 | if is_part and active:
350 | base_has_active[base] = True
351 |
352 | # Apply "active-only" filter while keeping active bases visible if any child is active
353 | term_rows = []
354 | for (name, base, is_part, active, line) in rows_buf:
355 | if show_active_only:
356 | if not (active or (not is_part and base_has_active.get(name, False))):
357 | continue
358 | term_rows.append((name, line))
359 |
360 | # Render
361 | stdscr.erase()
362 | draw_header(stdscr, args.refresh) # your existing header function
363 | # Optionally show toggle states on the right:
364 | try:
365 | hdr = f" hide zeros: {'on' if hide_zeros else 'off'} | active-only: {'on' if show_active_only else 'off'} "
366 | y, x = 0, max(0, curses.COLS - len(hdr) - 1)
367 | stdscr.addstr(y, x, hdr)
368 | except curses.error:
369 | pass
370 |
371 | line_no = ROW0
372 | for _, line in term_rows:
373 | try:
374 | stdscr.addstr(line_no, 0, line)
375 | except curses.error:
376 | pass
377 | line_no += 1
378 |
379 | try:
380 | stdscr.addstr(line_no + 1, 0, "Press 'q' to quit | '0' toggle hide zeros | 'A' show active only")
381 | except curses.error:
382 | pass
383 |
384 | stdscr.refresh()
385 |
386 | # Key handling with timeout until next refresh
387 | t_end = time.monotonic() + args.refresh
388 | while True:
389 | ch = stdscr.getch()
390 | if ch in (ord('q'), ord('Q')):
391 | return
392 | elif ch == ord('0'):
393 | hide_zeros = not hide_zeros
394 | break
395 | elif ch in (ord('A'), ord('a')):
396 | show_active_only = not show_active_only
397 | break
398 |
399 | if time.monotonic() >= t_end:
400 | break
401 | time.sleep(0.02)
402 |
403 | if __name__ == "__main__":
404 | args = parse_args()
405 | set_locale(args.locale)
406 |
407 | # Basic sanity: require /proc/diskstats
408 | if not os.path.exists(DISKSTATS):
409 | print(f"{DISKSTATS} not found. Are you on Linux?", file=sys.stderr)
410 | sys.exit(1)
411 |
412 | try:
413 | curses.wrapper(main, args)
414 | except KeyboardInterrupt:
415 | pass
416 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------