diff --git a/README.md b/README.md index 0da12ff..9214454 100644 --- a/README.md +++ b/README.md @@ -1,194 +1,93 @@ -# bashtop +# ![bashtop](logo-t.png) -**Version:** 0.6.2 -**Usage:** Tmp -**Language:** Bash & Python +**Usage:** Linux resource monitor +**Language:** Bash ## Description -Internet speeds are tested against random servers from speedtest.net at an timed interval (defined by user). -If slow speed (defined by user) is detected, then runs a number of download and upload test with optional route tests to servers and writes to a logfile. - -## Screenshots - -Main UI with graph on. - -![Screenshot 1]() +Resource monitor that shows usage and stats for processor, memory, disks, network and processes. ## Features -* Function for checking current bandwidth usage to block false positives. Can optionally collect bandwidth usage from a router running linux with a ssh server enabled. -* Scrollable graph showing speeds over time. -* All output in the UI is also scrollabe with UP, DOWN, PAGE_UP, PAGE_DOWN, HOME, END keys working as you would expect. -* Option to have the timer reset on any mouse or keyboard activity in XServer. -* Option to have the timer auto pause when monitor is on and unpause when monitor is off. -* Unit used can be switched between Mbits and MB/s. -* Function for splitting log files at a defined size, old log files can also be automatically compressed. +* Easy to use, with a game inspired menu system. +* Fast and responsive UI with UP, DOWN keys process selection. +* Function for showing detailed stats for selected process. +* Ability to filter processes. +* Easy switching between sorting options. +* Send SIGTERM, SIGKILL, SIGINT to selected process. +* UI menu for changing all config file options. +* Auto scaling graph for network usage. + +## Screenshots + +Main UI showing details for a selected process. +![Screenshot 1](main.png) + +Main menu. +![Screenshot 2](menu.png) + +Options menu. +![Screenshot 3](options.png) + ## Configurability -Config files stored in "$HOME/.config/spdtest" folder +All options changeable from within UI. +Config files stored in "$HOME/.config/bashtop" folder -#### spdtest.cfg: (auto generated if not found) +#### bashtop.cfg: (auto generated if not found) ```bash -net_device="auto" #* Network interface to get current speed from, set to "auto" to get default interface from "ip route" command -unit="megabit" #* Default speed value to use, valid values are "megabit" and "megabyte" -slowspeed="30" #* Download speed in unit defined above that triggers more tests, recommended set to 10%-40% of your max speed -numservers="30" #* How many of the closest servers to get from speedtest.net, used as random pool of servers to test against -slowretry="1" #* When speed is below slowspeed, how many retries of random servers before running full tests -max_err_retry="3" #* Max servers to test if an error is encountered in slowcheck -numslowservers="8" #* How many of the closest servers from list to test if slow speed has been detected, tests all if not set -precheck="true" #* Check current bandwidth usage before slowcheck, blocks if speed is higher then values set below -precheck_samplet="5" #* Time in seconds to sample bandwidth usage, defaults to 5 if not set -precheck_down="50" #* Download speed in unit defined above that blocks slowcheck -precheck_up="50" #* Upload speed in unit defined above that blocks slowcheck -precheck_ssh_host="192.168.1.1" #* If set, precheck will fetch data from /proc/net/dev over SSH, for example from a router running linux - #* remote machine needs to have: "/proc/net/dev" and be able to run commands "ip route" and "grep" - #* copy SSH keys to remote machine if you don't want to be asked for password at start, guide: https://www.ssh.com/ssh/copy-id -precheck_ssh_user="admin" #* Username for ssh connection -precheck_ssh_nd="auto" #* Network interface on remote machine to get speeds from, set to "auto" if unsure -waittime="00:20:00" #* Default wait timer between slowchecks, format: "HH:MM:SS" -slowwait="00:10:00" #* Time between tests when slow speed has been detected, uses wait timer if unset, format: "HH:MM:SS" -idle="false" #* If "true", resets timer if keyboard or mouse activity is detected in XServer -# idletimer="00:30:00" #* If set and idle="true", the script uses this timer until first test, then uses standard wait time, - #* any X Server activity resets back to idletimer, format: "HH:MM:SS" -displaypause="false" #* If "true" automatically pauses timer when display is on, unpauses when off, overrides idle="true" if set, needs xset to work -paused="false" #* If "true", the timer is paused at startup, ignored if displaypause="true" -startuptest="false" #* If "true" and paused="false", tests speed at startup before timer starts -main_menu_start="shown" #* The status of the main menu at start, possible values: "shown", "hidden" -graph_start="shown" #* The status of the speed graph at start, possible values: "shown", "hidden" -loglevel="2" #* 0 : No logging - #* 1 : Log only when slow speed has been detected - #* 2 : Also log slow speed check - #* 3 : Also log server updates - #* 4 : Log all including forced tests -logdir="$HOME/spdtest-logs" #* Logfile save directory -quiet_start="true" #* If "true", don't print serverlist and routelist at startup -maxlogsize="1024" #* Max logsize (in kilobytes) before log is split -logcompress="gzip" #* Command for compressing logs, only log splits beyond the last split is compressed, disabled if not set -# custom_log="" #* Custom logfile (full path), if a custom logfile is set log splitting is disabled -max_buffer="1000" #* Max number of lines to buffer in internal scroll buffer -buffer_save="true" #* Save buffer to disk on exit and restore on start -mtr="true" #* Set "false" to disable route testing with mtr, automatically set to "false" if mtr is not found in PATH -mtr_internal="true" #* Use hosts from full test with speeds below $slowspeed in mtr test -mtr_internal_ok="false" #* Use hosts from full test with speeds above $slowspeed in mtr test -# mtr_internal_max="" #* Set max hosts to add from full test -mtr_external="false" #* Use hosts from route.cfg, see route.cfg.sample for formatting -mtrpings="25" #* Number of pings sent with mtr -testonly="false" #* If "true", never enter UI mode, always run full tests and quit -testnum="1" #* Number of times to loop full tests in testonly mode +#? Config file for bashtop v. 0.6.5 -ookla_speedtest="speedtest" #* Command or full path to official speedtest client +#* Update time in milliseconds, increases automatically if set below internal loops processing time, recommended 2000 ms or above for better sample times for graphs +update_ms="2500" -trace_errors="true" #* In event of error print line number of offending command to $HOME/.config/spdtest/errors +#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu responsive" +#* "cpu lazy" upates sorting over time, "cpu responsive" updates sorting directly at a cpu usage cost +proc_sorting="cpu lazy" + +#* Reverse sorting order, "true" or "false" +proc_reversed="false" + +#* Check cpu temperature, only works if "sensors" command is available and have values for "Package" and "Core" +check_temp="true" + +#* Draw a clock at top of screen, formatting according to strftime, empty string to disable +draw_clock="%X" + +#* Custom cpu model name, empty string to disable +custom_cpu_name="" + +#* Enable error logging to "$HOME/.config/bashtop/error.log", "true" or "false" +error_logging="true" ``` -#### route.cfg.sample: (rename to route.cfg to use additional hosts in route test) - -```bash -#? List of routes to test with mtr -#? Format: -#? routelista+=("host") -#? routelistdesc["host"]=("Name") -#? routelistport["host"]=("port") 'Set port to "auto" if you don't want to set a custom port!' - -routelista+=("google.com") -routelistdesc["google.com"]="Google" -routelistport["google.com"]="auto" - -routelista+=("reddit.com") -routelistdesc["reddit.com"]="Reddit" -routelistport["reddit.com"]="auto" - -routelista+=("twitch.tv") -routelistdesc["twitch.tv"]="Twitch" -routelistport["twitch.tv"]="auto" - -routelista+=("amazon.com") -routelistdesc["amazon.com"]="Amazon" -routelistport["amazon.com"]="auto" -``` - -#### Command line options: (to be updated) +#### Command line options: (not yet implemented) ``` -USAGE: ./spdtest.sh [OPTIONS] +USAGE: bashtop -OPTIONS: - -t, --test [num] Runs full test 1 or number of times and quits - -u, --unit megabit/megabyte Which unit to show speed in, [default: megabit] - -s, --slow-speed speed Defines what speed in defined unit that will trigger more tests - -n, --num-servers num How many of the closest servers to get from speedtest.net - -i, --interface name Network interface being used [default: auto] - -l, --loglevel 0-3 0 No logging - 1 Log only when slow speed has been detected - 2 Also log slow speed check and server update - 3 Log all including forced tests - -lf, --log-file file Full path to custom logfile, no log rotation is done on custom logfiles - -p, --paused Sets timer to paused state at startup - -wt, --wait-time HH:MM:SS Time between tests when NO slowdown is detected [default: 00:10:00] - -st, --slow-time HH:MM:SS Time between tests when slowdown has been detected, uses wait timer if unset - -x, --x-reset [HH:MM:SS] Reset timer if keyboard or mouse activity is detected in X Server - If HH:MM:SS is included, the script uses this timer until first test, then uses - standard wait time, any activity resets to idle timer [default: unset] - -d, --display-pause Automatically pauses timer when display is on, unpauses when off - -gs, --gen-server-cfg num Writes number of the closest servers to "server.cfg" and quits - Servers aren't updated automatically at start if "server.cfg" exists - -sc, --server-config file Reads server config from [default: server.cfg] - If used in combination with -gs a new file is created - -h, --help Shows help information -CONFIG: - Note: All config files are stored in: $HOME/.config/spdtest - spdtest.cfg Automatically created with default values if removed - [server.cfg] Stores server id's to use with speedtest, delete to refresh servers on start - [route.cfg] Additional hosts to test with mtr, see route.cfg.sample for formatting -LOG: - Logfile location can be changed in config file - Currently: $HOME/spdtest-logs ``` +## Compability + +Should work on most modern linux distributions. + + + ## Dependencies -**bash** (v4.4 or later) Script functionality might brake with earlier versions. +**bash** (v4.4 or later) Script functionality will most probably brake with earlier versions. +Bash version 5 is higly recommended for a -**[Python 3](https://www.python.org/downloads)** (v3.7 or later) Needed for speedtest-cli, grc and getIdle. - -**[speedtest](https://www.speedtest.net/apps/cli)** Official speedtest client from Ookla, needs to be in path or defined in config. - -**[jq](https://stedolan.github.io/jq/)** Needed for json parsing. - -## Included - -**[speedtest-cli](https://github.com/sivel/speedtest-cli)** Used to get serverlist, since official speedtest client from Ookla is limited to 10 servers. -Modified and heavily stripped down, based on version 2.1.2. Python code included in the script. - -**[grc](https://github.com/garabik/grc)** For making text output in the UI pretty. -Modified version of grcat. Python code included in the script. - -**getIdle** Get XServer idle time. Python code included in script. - -## Optionals - -**[mtr](https://github.com/traviscross/mtr)** Needed if you want to check routes to slow servers. - -**[less](http://www.greenwoodsoftware.com/less/)** Needed if you want option to view logfile from UI. ## TODO -- [ ] TODO Fix argument parsing and error messages -- [ ] TODO Change slowtest to multiple servers and compare results -- [ ] TODO fix wrong keypress in inputwait, esc codes etc -- [ ] TODO fix up README.md -- [x] TODO extern config and save to config? -- [x] TODO ssh controlmaster, server, client for precheck_speed -- [ ] TODO buffer logview -- [ ] TODO route test menu, choose host to test -- [ ] TODO windows: help, options, route -- [x] TODO plot speedgraphs overtime in UI -- [ ] TODO stat file -- [ ] Everything else... +- [ ] TODO Add command line argument parsing. +- [ ] TODO Miscellaneous optimizations and code cleanup + ## LICENSE [Apache License 2.0](LICENSE) diff --git a/bashtop b/bashtop index 8d85d74..d59d498 100755 --- a/bashtop +++ b/bashtop @@ -21,7 +21,7 @@ banner=( "██╔══██╗██╔══██║╚════██║██╔══██║ ██║ ██║ ██║██╔═══╝ " "██████╔╝██║ ██║███████║██║ ██║ ██║ ╚██████╔╝██║ " "╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ") -declare version="0.6.5" +declare version="0.6.8" declare banner_width=${#banner[0]} banner_colors=("#E62525" "#CD2121" "#B31D1D" "#9A1919" "#801414") @@ -93,7 +93,7 @@ declare -a sorting=( "pid" "program" "arguments" "threads" "user" "memory" "cpu declare -a pid_history detail_graph detail_history detail_mem_history declare time_left timestamp_start timestamp_end timestamp_input_start timestamp_input_end time_string mem_out proc_misc prev_screen pause_screen filter input_to_filter declare no_epoch proc_det proc_misc2 sleeping=0 detail_mem_graph proc_det2 proc_out -declare esc_character tab backspace update_sec sleepy late_update skip_process_draw winches +declare esc_character tab backspace update_sec sleepy late_update skip_process_draw winches quitting declare -a disks_free disks_total disks_name disks_free_percent saved_key printf -v esc_character "\u1b" printf -v tab "\u09" @@ -2878,9 +2878,110 @@ options_() { #? Shows the options overlay elif [[ -n ${pause_screen} ]]; then unpause_; draw_update_string; fi } +killer_() { #? Kill process with selected signal + local kill_op="$1" kill_pid="$2" killer_out killer_box col line program keypress selected selected_int=0 sig confirmed=0 option killer_pause status status_fg msg + local -a options=("yes" "no") + + if ! program="$(ps -o comm --no-header -p ${kill_pid})"; then return; fi + + case $kill_op in + t|T) kill_op="terminate"; sig="SIGTERM" ;; + k|K) kill_op="kill"; sig="SIGKILL" ;; + i|I) kill_op="interrupt"; sig="SIGINT" ;; + esac + + until false; do + + #* Put program to sleep if caught ctrl-z + if ((sleepy==1)); then sleep_; fi + + draw_clock + pause_ killer_pause + + if [[ -z $killer_box ]]; then + col=$((tty_width/2-15)); line=$((tty_height/2-4)); y=1 + unset redraw killer_box + create_box -v killer_box -w 40 -h 9 -l $line -c $((col++)) -fill -lc "#923535" -title "${kill_op}" + fi + + if ((confirmed==0)); then + selected="${options[selected_int]}" + print -v killer_out -m $((line+2)) $col -fg ee -b -jc 38 -t "${kill_op^} ${program::20}?" -m $((line+4)) $((col+3)) + for option in "${options[@]}"; do + if [[ $option == "${selected}" ]]; then print -v killer_out -bg "#923535"; fi + print -v killer_out -fg cc -b -r 5 -t "[ ${option^} ]" -rs + done + + elif ((confirmed==1)); then + selected="ok" + print -v killer_out -m $((line+2)) $col -fg ee -b -jc 38 -t "Sending signal ${sig} to pid ${kill_pid}!" + print -v killer_out -m $((line+4)) $col -fg ${status_fg} -jc 38 -t "${status^}!" -m $((line+6)) $col + if [[ -n $msg ]]; then print -v killer_out -m $((line+5)) $col -fg ee -jc 38 -t "${msg}" -m $((line+7)) $col; fi + print -v killer_out -fg cc -bg "#923535" -b -r 15 -t "[ Ok ]" -rs + fi + + echo -en "${killer_pause}${killer_box}${killer_out}" + unset killer_out draw_out + + + get_ms timestamp_end + time_left=$((timestamp_start+update_ms-timestamp_end)) + if ((time_left>1000)); then wait_string=1; time_left=$((time_left-1000)) + elif ((time_left>1)); then printf -v wait_string ".%03d" "${time_left}"; time_left=0 + else wait_string="0.001"; time_left=0; fi + + get_key -v keypress -w ${wait_string} + if [[ $(stty size) != "$tty_height $tty_width" ]]; then resized; fi + if ((resized>0)); then + calc_sizes; draw_bg quiet; time_left=0; unset killer_out killer_box + fi + + case "$keypress" in + right|shift_tab) if ((selected_int>0)); then ((selected_int--)); else selected_int=$((${#options[@]}-1)); fi ;; + left|tab) if ((selected_int<${#options[@]}-1)); then ((++selected_int)); else selected_int=0; fi ;; + enter) + case "$selected" in + yes) confirmed=1 ;; + no|ok) confirmed=-1 ;; + esac + ;; + q|Q) quit_ ;; + esac + + + + if ((confirmed<0)); then + unpause_ + break + elif ((confirmed>0)) && [[ -z $status ]]; then + if kill -${sig} ${kill_pid} >/dev/null 2>&1; then + status="success" + status_fg="30ee20" + else + if ! ps -p ${kill_pid} >/dev/null 2>&1; then + msg="Process not running." + elif [[ $UID != 0 ]]; then + msg="Try restarting with sudo." + else + msg="Unknown error." + fi + status="failed"; status_fg="ee3020"; fi + fi + + + if ((time_left==0)); then get_ms timestamp_start; unset draw_out; collect_and_draw; fi + if ((resized>=5)); then resized=0; fi + + done + + +} + get_key() { #? Get one key from standard input and translate key code to readable format local key key_out wait_time esc ext_out save + if ((quitting==1)); then quit_; fi + until (($#==0)); do case "$1" in -v|-variable) local -n key_out=$2; ext_out=1; shift;; #* Output variable @@ -3075,6 +3176,14 @@ process_input() { #? Process keypresses for main ui unset input_to_filter filter filter_change=1 fi + ;; + t|T|k|K|i|I) + if [[ ${proc[selected]} -gt 0 ]]; then + killer_ "$keypress" "${proc[selected_pid]}" + elif [[ ${proc[detailed]} -eq 1 && -z ${proc[detailed_killed]} ]]; then + killer_ "$keypress" "${proc[detailed_pid]}" + fi + ;; # *) # #if [[ $keypress == "" ]]; then keypress="enter"; fi # printf -v letter_hex '%X\n' "'$keypress" @@ -3253,9 +3362,9 @@ else fi #* Set up traps for ctrl-c, soft kill, window resize and resume from ctrl-z -trap 'quit_' SIGINT SIGQUIT SIGTERM -trap 'resized=1' SIGWINCH -trap 'sleepy=1; time_left=0' SIGTSTP +trap 'quitting=1; time_left=0' SIGINT SIGQUIT SIGTERM +trap 'resized=1; time_left=0' SIGWINCH +trap 'sleepy=1; time_left=0' SIGTSTP trap 'resume_' SIGCONT @@ -3263,7 +3372,7 @@ trap 'resume_' SIGCONT #* Set up error logging to file if enabled if [[ $error_logging == true ]]; then set -o errtrace - trap traperr ERR + trap 'traperr' ERR ( echo " " ; echo "New instance of $0 Pid: $$" ) >> "${config_dir}/error.log" exec 2>>"${config_dir}/error.log" # exec 19>"${config_dir}/tracing.log"