Add percentage of max to tdu
[khome.git] / home / lib / login_functions.sh
index 2a90695..8e25bc0 100644 (file)
@@ -140,17 +140,29 @@ top_commands() {
 # TODO: Consider using numfmt instead of awk
 tdu() {
     du "$1" \
-    | sort -n -k 1 \
-    | tail -50 \
     | awk '
         {
             size = $1
             path = $0
             sub("^" $1 "\t+", "", path)
-            gb = size / 1024 / 1024
-            printf("%f\t%s\n", gb, path)
-        }' \
-    | cut -c 1-115
+            paths[path] = size
+            if (size > max)
+                max = size
+        }
+
+        END {
+            for (path in paths) {
+                size = paths[path]
+                pct = 100 * (size / max)
+                gb = size / 1024 / 1024
+                printf("%6.2f %3d%% %s\n", gb, pct, path)
+            }
+        }
+    ' \
+    | sort -r -n -k 1 \
+    | head -50 \
+    | tac
+    # A slight optimization: head can exit before traversing the full input.
 }
 
 # Top Disk-Using Files
@@ -225,8 +237,9 @@ man() {
     command man "$@"
 }
 
-experiment() {
-    cd "$(~/bin/experiment $@)" || kill -INT $$
+# new experiment
+x() {
+    cd "$(~/bin/x $@)" || kill -INT $$
 }
 
 hump() {
@@ -234,13 +247,13 @@ hump() {
 }
 
 howto() {
-    cat "$(find  ~/Archives/Documents/HOWTOs -mindepth 1 -maxdepth 1 | sort | fzf)"
+    cat "$(find  ~/arc/doc/HOWTOs -mindepth 1 -maxdepth 1 | sort | fzf)"
 }
 
 _yt() {
     local -r base_dir="$1"
-    local -r opts="$2"
-    local -r uri="$3"
+    local -r uri="$2"
+    local -r opts="$3"
 
     local -r id=$(youtube-dlc --get-id "$uri")
     local -r title=$(youtube-dlc --get-title "$uri" | sed 's/[^A-Za-z0-9._-]/_/g')
@@ -254,7 +267,7 @@ _yt() {
 
 yt_audio() {
     local -r uri="$1"
-    _yt "${DIR_YOUTUBE_AUDIO}/individual" '-f 140' "$uri"
+    _yt "${DIR_YOUTUBE_AUDIO}/individual" "$uri" '-f 140'
 }
 
 yt_video() {
@@ -263,13 +276,17 @@ yt_video() {
 }
 
 gh_fetch_repos() {
-    curl "https://api.github.com/$1/$2/repos?page=1&per_page=10000"
+    local -r user_type="$1"
+    local -r user_name="$2"
+
+    curl "https://api.github.com/$user_type/$user_name/repos?page=1&per_page=10000"
 }
 
 gh_clone() {
-    gh_user_type="$1"
-    gh_user_name="$2"
-    gh_dir="${DIR_GITHUB}/${gh_user_name}"
+    local -r gh_user_type="$1"
+    local -r gh_user_name="$2"
+
+    local -r gh_dir="${DIR_GITHUB}/${gh_user_name}"
     mkdir -p "$gh_dir"
     cd "$gh_dir" || kill -INT $$
     gh_fetch_repos "$gh_user_type" "$gh_user_name" \
@@ -317,7 +334,7 @@ EOF
 
 work_log() {
     mkdir -p "$DIR_WORK_LOG"
-    file_work_log_today="${DIR_WORK_LOG}/$(date +%F).md"
+    local -r file_work_log_today="${DIR_WORK_LOG}/$(date +%F).md"
     if [ ! -f "$file_work_log_today" ]
     then
         work_log_template > "$file_work_log_today"
@@ -340,23 +357,29 @@ weather() {
     curl "http://wttr.in/$_weather_location?format=v2"
 }
 
+_bt_devs_infos() {
+    # grep's defintion of a line does not include \r, wile awk's does and
+    # which bluetoothctl outputs
+    awk '/^Device +/ {print $2}' \
+    | xargs -I% sh -c 'echo info % | bluetoothctl' \
+    | awk '/^Device |^\t[A-Z][A-Za-z0-9]+: /'
+}
+
 bt_devs_paired() {
-    bluetoothctl -- paired-devices \
-    | awk '{print $2}' \
-    | xargs bluetoothctl -- info
+    echo 'paired-devices' | bluetoothctl | _bt_devs_infos
 }
 
 bt_devs() {
-    bluetoothctl -- devices \
-    | awk '{print $2}' \
-    | xargs bluetoothctl -- info
+    echo 'devices' | bluetoothctl | _bt_devs_infos
 }
 
 run() {
-    stderr="$(mktemp)"
+    local -r stderr="$(mktemp)"
+
+    local code urgency
+
     $@ 2> >(tee "$stderr")
     code="$?"
-    urgency=''
     case "$code" in
         0) urgency='normal';;
         *) urgency='critical'
@@ -366,21 +389,36 @@ run() {
 }
 
 bar_gauge() {
-    local -r width="$1"
+    awk "$@" '
+        BEGIN {
+            # CLI options
+            width    = width    ? width    : 80
+            ch_left  = ch_left  ? ch_left  : "["
+            ch_right = ch_right ? ch_right : "]"
+            ch_blank = ch_blank ? ch_blank : "-"
+            ch_used  = ch_used  ? ch_used  : "|"
+            num      = num      ? 1        : 0
+            pct      = pct      ? 1        : 0
+        }
 
-    awk -v width="$width" '
         {
-            used  = $1
-            total = $2
-
-            u = num_scale(used, total, 1, width)
-
-            printf "["
+            cur = $1
+            max = $2
+            lab = $3
+
+            cur_scaled = num_scale(cur, max, 1, width)
+
+            printf \
+                "%s%s%s%s", \
+                lab ? lab         " " : "", \
+                num ? cur "/" max " " : "", \
+                pct ? sprintf("%3.0f%% ", cur / max * 100) : "", \
+                ch_left
             for (i=1; i<=width; i++) {
-                c = i <= u ? "|" : " "
-                printf "%c", c
+                c = i <= cur_scaled ? ch_used : ch_blank
+                printf "%s", c
             }
-            printf "]\n"
+            printf "%s\n", ch_right
         }
 
         function num_scale(src_cur, src_max, dst_min, dst_max) {
@@ -389,56 +427,284 @@ bar_gauge() {
     '
 }
 
-motd() {
+flat_top_5() {
+    sort -n -k 1 -r \
+    | head -5 \
+    | awk '
+        {
+            cur  = $1
+            max  = $2
+            name = $3
+            pct  = cur / max * 100
+            printf "%s%s %.2f%%", sep, name, pct
+            sep = ",  "
+        }
+
+        END {printf "\n"}
+        '
+}
+
+internet_addr() {
+    curl --silent --show-error --max-time "${1:=1}" 'https://api.ipify.org' 2>&1
+}
+
+status_batt() {
+    case "$(uname)" in
+        'Linux')
+            if which upower > /dev/null
+            then
+                upower --dump \
+                | awk '
+                    /^Device:[ \t]+/ {
+                        device["path"] = $2
+                        next
+                    }
+
+                    /  battery/ && device["path"] {
+                        device["is_battery"] = 1
+                        next
+                    }
+
+                    /    percentage:/ && device["is_battery"] {
+                        device["battery_percentage"] = $2
+                        sub("%$", "", device["battery_percentage"])
+                        next
+                    }
+
+                    /^$/ {
+                        if (device["is_battery"] && device["path"] == "/org/freedesktop/UPower/devices/DisplayDevice")
+                            print device["battery_percentage"], 100, "batt"
+                        delete device
+                    }
+                '
+            fi
+        ;;
+    esac
+}
+
+indent() {
+    awk -v unit="$1" '{printf "%s%s\n", unit, $0}'
+}
+
+status() {
+    local -r indent_unit='    '
+
     uname -srvmo
     hostname | figlet
     uptime
 
     echo
 
-    printf 'mem  '
-    free \
-    | awk '$1 == "Mem:" {total=$2; used=$3; print used, total}' \
-    | bar_gauge 73
+    echo 'accounting'
 
-    printf 'disk '
-    df ~ \
-    | awk 'NR == 2 {used=$3; avail=$4; total=used+avail; print used, total}' \
-    | bar_gauge 73
+    printf '%stmux\n%ssessions %d, clients %d\n' \
+        "$indent_unit" \
+        "${indent_unit}${indent_unit}" \
+        "$(tmux list-sessions 2> /dev/null | wc -l)" \
+        "$(tmux list-clients  2> /dev/null | wc -l)"
 
-    case "$(uname)" in
-        'Linux')
-            printf 'batt '
-            upower --dump \
-            | awk '
-                /^Device:[ \t]+/ {
-                    device["path"] = $2
-                    next
-                }
+    echo
 
-                /  battery/ && device["path"] {
-                    device["is_battery"] = 1
-                    next
-                }
+    printf '%sprocs by user\n' "${indent_unit}"
+    ps -eo user \
+    | awk '
+        NR > 1 {
+            count_by_user[$1]++
+            total++
+        }
 
-                /    percentage:/ && device["is_battery"] {
-                    device["battery_percentage"] = $2
-                    sub("%$", "", device["battery_percentage"])
-                    next
-                }
+        END {
+            for (user in count_by_user)
+                print count_by_user[user], total, user
+        }
+        ' \
+    | flat_top_5 \
+    | indent "${indent_unit}${indent_unit}"
 
-                /^$/ {
-                    if (device["is_battery"] && device["path"] == "/org/freedesktop/UPower/devices/DisplayDevice")
-                        print device["battery_percentage"], 100
-                    delete device
+    echo
+
+    echo 'resources'
+    (
+        free | awk '$1 == "Mem:" {print $3, $2, "mem"}'
+        df ~ | awk 'NR == 2 {print $3, $3 + $4, "disk"}'
+        status_batt
+    ) \
+    | bar_gauge -v width=60 -v pct=1 \
+    | column -t \
+    | indent "$indent_unit"
+
+    echo
+
+    printf '%smem by proc\n' "$indent_unit"
+    ps -eo rss,cmd \
+    | awk -v total="$(free | awk '$1 == "Mem:" {print $2; exit}')" '
+        NR > 1 {
+            rss = $1
+            cmd = $2
+            n = split(cmd, path, "/")  # _may_ be a path
+            proc = path[n]
+            by_proc[proc] += rss
+        }
+
+        END {
+            for (proc in by_proc)
+                print by_proc[proc], total, proc
+            }
+        ' \
+        | flat_top_5 \
+        | indent "${indent_unit}${indent_unit}"
+
+    echo
+
+    printf '%sthermal\n' "$indent_unit"
+    for _dir in /sys/class/thermal/thermal_zone*
+    do
+        printf '%s %.2f C\n' \
+            $(cat "$_dir"/type) \
+            $(( $(cat "$_dir"/temp) / 1000 ))
+    done \
+    | column -t \
+    | indent "${indent_unit}${indent_unit}"
+
+    echo 'net'
+    local -r internet_addr=$(internet_addr 0.5)
+    local -r internet_ptr=$(host -W 1 "$internet_addr" | awk 'NR == 1 {print $NF}' )
+
+    echo "${indent_unit}internet"
+    echo "${indent_unit}${indent_unit}$internet_addr  $internet_ptr"
+    echo "${indent_unit}if"
+    (ifconfig; iwconfig) 2> /dev/null \
+    | awk '
+        /^[^ ]/ {
+            device = $1
+            sub(":$", "", device)
+            if ($4 ~ "ESSID:") {
+                _essid = $4
+                sub("^ESSID:\"", "", _essid)
+                sub("\"$", "", _essid)
+                essid[device] = _essid
+            }
+            next
+        }
+
+        /^ / && $1 == "inet" {
+            address[device] = $2
+            next
+        }
+
+        /^ +Link Quality=[0-9]+\/[0-9]+ +Signal level=/ {
+            split($2, lq_parts_eq, "=")
+            split(lq_parts_eq[2], lq_parts_slash, "/")
+            cur = lq_parts_slash[1]
+            max = lq_parts_slash[2]
+            link[device] = cur / max * 100
+            next
+        }
+
+        END {
+            for (device in address)
+                if (device != "lo") {
+                    l = link[device]
+                    e = essid[device]
+                    l = l ? sprintf("%.0f%%", l) : "--"
+                    e = e ? e : "--"
+                    print device, address[device], e, l
                 }
-            ' \
-            | bar_gauge 73
-        ;;
-    esac
+        }
+        ' \
+    | column -t \
+    | indent "${indent_unit}${indent_unit}"
 
-    #echo
-    # TODO: netstat summary
     # WARN: ensure: $USER ALL=(ALL) NOPASSWD:/bin/netstat
-    #sudo -n netstat -tulpn | awk '/^udp/ && !first++ {printf "\n"} 1'
+
+    echo "${indent_unit}-->"
+
+    sudo -n netstat -tulnp \
+    | awk -v indent="${indent_unit}${indent_unit}" '
+        NR > 2 && ((/^tcp/ && proc = $7) || (/^udp/ && proc = $6)) {
+           protocol = $1
+           addr = $4
+           port = a[split(addr, a, ":")]
+           name = p[split(proc, p, "/")]
+           names[name] = 1
+           protocols[protocol] = 1
+           if (!seen[protocol, name, port]++)
+               ports[protocol, name, ++seen[protocol, name]] = port
+       }
+
+       END {
+           for (protocol in protocols) {
+               printf "%s%s\t", indent, toupper(protocol)
+               for (name in names) {
+                   if (n = seen[protocol, name]) {
+                       sep = ""
+                       printf "%s:", name
+                       for (i = 1; i <= n; i++) {
+                           printf "%s%d", sep, ports[protocol, name, i]
+                           sep = ","
+                       }
+                       printf "  "
+                   }
+               }
+               printf "\n"
+           }
+       }'
+
+    echo "${indent_unit}<->"
+
+    printf '%sTCP: ' "${indent_unit}${indent_unit}"
+    sudo -n netstat -tnp \
+    | awk 'NR > 2 && $6 == "ESTABLISHED" {print $7}' \
+    | awk -F/ '{print $2}' \
+    | sort -u \
+    | xargs \
+    | column -t
+
+    # TODO: iptables summary
+}
+
+ssh_invalid_attempts_from() {
+    awk '
+        /: Invalid user/ && $5 ~ /^sshd/ {
+            u=$8
+            addr=$10 == "port" ? $9 : $10
+            max++
+            curr[addr]++
+        }
+
+        END {
+            for (addr in curr)
+                if ((c = curr[addr]) > 1)
+                    print c, max, addr
+        }
+        ' \
+        /var/log/auth.log \
+        /var/log/auth.log.1 \
+    | sort -n -k 1 \
+    | bar_gauge -v width="$(stty size | awk '{print $2}')" -v num=1 -v ch_right=' ' -v ch_left=' ' -v ch_blank=' ' \
+    | column -t
+}
+
+loggers() {
+    awk '
+        {
+            split($5, prog, "[")
+            sub(":$", "", prog[1]) # if there were no [], than : will is left behind
+            print prog[1]
+        }' /var/log/syslog /var/log/syslog.1 \
+    | awk '
+        {
+            n = split($1, path, "/")  # prog may be in path form
+            prog = path[n]
+            total++
+            count[prog]++
+        }
+
+        END {
+            for (prog in count)
+                print count[prog], total, prog
+        }' \
+    | sort -n -k 1 \
+    | bar_gauge -v num=1 -v ch_right=' ' -v ch_left=' ' -v ch_blank=' ' \
+    | column -t
 }
This page took 0.054955 seconds and 4 git commands to generate.