2021-06-19 14:57:27 +02:00
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License , Version 2.0 ( the " License " ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an " AS IS " BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
indent = tab
tab - size = 4
*/
# include <array>
# include <ranges>
# include <atomic>
# include <fstream>
2021-06-19 22:48:31 +02:00
# include <btop_config.hpp>
2021-06-20 00:49:13 +02:00
# include <btop_shared.hpp>
2021-06-19 22:48:31 +02:00
# include <btop_tools.hpp>
2021-06-19 14:57:27 +02:00
2021-07-21 03:17:34 +02:00
using std : : array , std : : atomic ;
2021-06-19 14:57:27 +02:00
namespace fs = std : : filesystem ;
namespace rng = std : : ranges ;
using namespace Tools ;
//* Functions and variables for reading and writing the btop config file
namespace Config {
2021-07-21 03:17:34 +02:00
atomic < bool > locked ( false ) ;
atomic < bool > writelock ( false ) ;
bool write_new ;
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
const vector < array < string , 2 > > descriptions = {
{ " color_theme " , " #* Full path to a bashtop/bpytop/btop++ formatted \" .theme \" file, \" Default \" and \" TTY \" for builtin themes. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " theme_background " , " #* If the theme set background should be shown, set to False if you want terminal background transparency. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " truecolor " , " #* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " force_tty " , " #* Set to true to force tty mode regardless if a real tty has been detected or not. \n "
" #* Will force 16-color mode and TTY theme, set all graph symbols to \" tty \" and swap out other non tty friendly symbols. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " graph_symbol " , " #* Default symbols to use for graph creation, \" braille \" , \" block \" or \" tty \" . \n "
" #* \" braille \" offers the highest resolution but might not be included in all fonts. \n "
" #* \" block \" has half the resolution of braille but uses more common characters. \n "
" #* \" tty \" uses only 3 different symbols but will work with most fonts and should work in a real TTY. \n "
" #* Note that \" tty \" only has half the horizontal resolution of the other two, so will show a shorter historical view. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " graph_symbol_cpu " , " # Graph symbol to use for graphs in cpu box, \" default \" , \" braille \" , \" block \" or \" tty \" . " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " graph_symbol_mem " , " # Graph symbol to use for graphs in cpu box, \" default \" , \" braille \" , \" block \" or \" tty \" . " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " graph_symbol_net " , " # Graph symbol to use for graphs in cpu box, \" default \" , \" braille \" , \" block \" or \" tty \" . " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " graph_symbol_proc " , " # Graph symbol to use for graphs in cpu box, \" default \" , \" braille \" , \" block \" or \" tty \" . " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " shown_boxes " , " #* Manually set which boxes to show. Available values are \" cpu mem net proc \" , separate values with whitespace. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " update_ms " , " #* Update time in milliseconds, increases automatically if set below internal loops processing time, recommended 2000 ms or above for better sample times for graphs. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " proc_update_mult " , " #* Processes update multiplier, sets how often the process list is updated as a multiplier of \" update_ms \" . \n "
" #* Set to 2 or higher to greatly decrease bpytop cpu usage. (Only integers). " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " proc_sorting " , " #* Processes sorting, \" pid \" \" program \" \" arguments \" \" threads \" \" user \" \" memory \" \" cpu lazy \" \" cpu responsive \" , \n "
" #* \" cpu lazy \" updates top process over time, \" cpu responsive \" updates top process directly. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " proc_reversed " , " #* Reverse sorting order, True or False. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " proc_tree " , " #* Show processes as a tree. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " proc_colors " , " #* Use the cpu graph colors in the process list. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " proc_gradient " , " #* Use a darkening gradient in the process list. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " proc_per_core " , " #* If process cpu usage should be of the core it's running on or usage of the total available cpu power. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " proc_mem_bytes " , " #* Show process memory as bytes instead of percent. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " cpu_graph_upper " , " #* Sets the CPU stat shown in upper half of the CPU graph, \" total \" is always available. \n "
" #* Select from a list of detected attributes from the options menu. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " cpu_graph_lower " , " #* Sets the CPU stat shown in lower half of the CPU graph, \" total \" is always available. \n "
" #* Select from a list of detected attributes from the options menu. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " cpu_invert_lower " , " #* Toggles if the lower CPU graph should be inverted. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " cpu_single_graph " , " #* Set to True to completely disable the lower CPU graph. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " show_uptime " , " #* Shows the system uptime in the CPU box. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " check_temp " , " #* Show cpu temperature. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " cpu_sensor " , " #* Which sensor to use for cpu temperature, use options menu to select from list of available sensors. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " show_coretemp " , " #* Show temperatures for cpu cores also if check_temp is True and sensors has been found. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " temp_scale " , " #* Which temperature scale to use, available values: \" celsius \" , \" fahrenheit \" , \" kelvin \" and \" rankine \" . " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " show_cpu_freq " , " #* Show CPU frequency. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " draw_clock " , " #* Draw a clock at top of screen, formatting according to strftime, empty string to disable. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " background_update " , " #* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " custom_cpu_name " , " #* Custom cpu model name, empty string to disable. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " disks_filter " , " #* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with a comma \" , \" . \n "
" #* Begin line with \" exclude= \" to change to exclude filter, otherwise defaults to \" most include \" filter. Example: disks_filter= \" exclude=/boot, /home/user \" . " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " mem_graphs " , " #* Show graphs instead of meters for memory values. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " show_swap " , " #* If swap memory should be shown in memory box. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " swap_disk " , " #* Show swap as a disk, ignores show_swap value above, inserts itself after first disk. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " show_disks " , " #* If mem box should be split to also show disks info. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " only_physical " , " #* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " use_fstab " , " #* Read disks list from /etc/fstab. This also disables only_physical. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " show_io_stat " , " #* Toggles if io stats should be shown in regular disk usage view. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " io_mode " , " #* Toggles io mode for disks, showing only big graphs for disk read/write speeds. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " io_graph_combined " , " #* Set to True to show combined read/write io graphs in io mode. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " io_graph_speeds " , " #* Set the top speed for the io graphs in MiB/s (10 by default), use format \" device:speed \" separate disks with a comma \" , \" . \n "
" #* Example: \" /dev/sda:100, /dev/sdb:20 \" . " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " net_download " , " #* Set fixed values for network graphs, default \" 10M \" = 10 Mibibytes, possible units \" K \" , \" M \" , \" G \" , append with \" bit \" for bits instead of bytes, i.e \" 100mbit \" . " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " net_upload " , " " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " net_auto " , " #* Start in network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " net_sync " , " #* Sync the scaling for download and upload to whichever currently has the highest scale. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " net_color_fixed " , " #* If the network graphs color gradient should scale to bandwidth usage or auto scale, bandwidth usage is based on \" net_download \" and \" net_upload \" values. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " net_iface " , " #* Starts with the Network Interface specified here. " } ,
2021-06-20 22:07:04 +02:00
2021-07-21 03:17:34 +02:00
{ " show_battery " , " #* Show battery stats in top right if battery is present. " } ,
2021-06-19 14:57:27 +02:00
2021-07-21 03:17:34 +02:00
{ " log_level " , " #* Set loglevel for \" ~/.config/bpytop/error.log \" levels are: \" ERROR \" \" WARNING \" \" INFO \" \" DEBUG \" . \n "
" #* The level set includes all lower levels, i.e. \" DEBUG \" will show all logging info. " }
} ;
2021-06-19 14:57:27 +02:00
2021-07-21 03:17:34 +02:00
unordered_flat_map < string , string > strings = {
{ " color_theme " , " Default " } ,
{ " shown_boxes " , " cpu mem net proc " } ,
{ " graph_symbol " , " braille " } ,
{ " graph_symbol_cpu " , " default " } ,
{ " graph_symbol_mem " , " default " } ,
{ " graph_symbol_net " , " default " } ,
{ " graph_symbol_proc " , " default " } ,
{ " proc_sorting " , " cpu lazy " } ,
{ " cpu_graph_upper " , " total " } ,
{ " cpu_graph_lower " , " total " } ,
{ " cpu_sensor " , " Auto " } ,
{ " temp_scale " , " celsius " } ,
{ " draw_clock " , " %X " } ,
{ " custom_cpu_name " , " " } ,
{ " disks_filter " , " " } ,
{ " io_graph_speeds " , " " } ,
{ " net_download " , " 10M " } ,
{ " net_upload " , " 10M " } ,
{ " net_iface " , " " } ,
{ " log_level " , " WARNING " } ,
{ " proc_filter " , " " } ,
{ " proc_command " , " " } ,
} ;
unordered_flat_map < string , string > stringsTmp ;
unordered_flat_map < string , bool > bools = {
{ " theme_background " , true } ,
{ " truecolor " , true } ,
{ " proc_reversed " , false } ,
{ " proc_tree " , false } ,
{ " proc_colors " , true } ,
{ " proc_gradient " , true } ,
{ " proc_per_core " , false } ,
{ " proc_mem_bytes " , true } ,
{ " cpu_invert_lower " , true } ,
{ " cpu_single_graph " , false } ,
{ " show_uptime " , true } ,
{ " check_temp " , true } ,
{ " show_coretemp " , true } ,
{ " show_cpu_freq " , true } ,
{ " background_update " , true } ,
{ " mem_graphs " , true } ,
{ " show_swap " , true } ,
{ " swap_disk " , true } ,
{ " show_disks " , true } ,
{ " only_physical " , true } ,
{ " use_fstab " , false } ,
{ " show_io_stat " , true } ,
{ " io_mode " , false } ,
{ " io_graph_combined " , false } ,
{ " net_color_fixed " , false } ,
{ " net_auto " , true } ,
{ " net_sync " , false } ,
{ " show_battery " , true } ,
{ " tty_mode " , false } ,
{ " force_tty " , false } ,
{ " lowcolor " , false } ,
{ " show_detailed " , false } ,
{ " proc_filtering " , false } ,
} ;
unordered_flat_map < string , bool > boolsTmp ;
unordered_flat_map < string , int > ints = {
{ " update_ms " , 2000 } ,
{ " proc_update_mult " , 2 } ,
{ " detailed_pid " , 0 } ,
{ " selected_pid " , 0 } ,
{ " proc_start " , 0 } ,
{ " proc_selected " , 0 } ,
} ;
unordered_flat_map < string , int > intsTmp ;
vector < string > valid_boxes = { " cpu " , " mem " , " net " , " proc " } ;
bool _locked ( const string & name ) {
writelock . wait ( true ) ;
if ( not write_new and rng : : find_if ( descriptions , [ & name ] ( const auto & a ) { return a . at ( 0 ) = = name ; } ) ! = descriptions . end ( ) )
write_new = true ;
return locked . load ( ) ;
2021-06-19 14:57:27 +02:00
}
fs : : path conf_dir ;
fs : : path conf_file ;
2021-07-18 15:44:32 +02:00
vector < string > current_boxes ;
2021-07-21 03:17:34 +02:00
void set ( string name , bool value ) {
2021-06-19 14:57:27 +02:00
if ( _locked ( name ) ) boolsTmp . insert_or_assign ( name , value ) ;
else bools . at ( name ) = value ;
}
2021-07-21 03:17:34 +02:00
void set ( string name , int value ) {
2021-06-19 14:57:27 +02:00
if ( _locked ( name ) ) intsTmp . insert_or_assign ( name , value ) ;
ints . at ( name ) = value ;
}
2021-07-21 03:17:34 +02:00
void set ( string name , string value ) {
2021-06-19 14:57:27 +02:00
if ( _locked ( name ) ) stringsTmp . insert_or_assign ( name , value ) ;
else strings . at ( name ) = value ;
}
2021-07-21 03:17:34 +02:00
void flip ( string name ) {
2021-06-19 14:57:27 +02:00
if ( _locked ( name ) ) {
2021-06-21 22:52:55 +02:00
if ( boolsTmp . contains ( name ) ) boolsTmp . at ( name ) = not boolsTmp . at ( name ) ;
else boolsTmp . insert_or_assign ( name , ( not bools . at ( name ) ) ) ;
2021-06-19 14:57:27 +02:00
}
2021-06-21 22:52:55 +02:00
else bools . at ( name ) = not bools . at ( name ) ;
2021-06-19 14:57:27 +02:00
}
2021-07-21 03:17:34 +02:00
void lock ( ) {
2021-07-18 15:44:32 +02:00
writelock . wait ( true ) ;
locked = true ;
2021-06-19 14:57:27 +02:00
}
2021-07-21 03:17:34 +02:00
void unlock ( ) {
2021-06-30 22:28:12 +02:00
if ( not locked ) return ;
2021-07-21 03:17:34 +02:00
writelock . wait ( true ) ;
2021-07-18 15:44:32 +02:00
writelock = true ;
2021-07-21 03:17:34 +02:00
try {
if ( Proc : : shown ) {
ints . at ( " selected_pid " ) = Proc : : selected_pid ;
ints . at ( " proc_start " ) = Proc : : start ;
ints . at ( " proc_selected " ) = Proc : : selected ;
}
2021-06-19 14:57:27 +02:00
2021-07-21 03:17:34 +02:00
for ( auto & item : stringsTmp ) {
strings . at ( item . first ) = item . second ;
}
stringsTmp . clear ( ) ;
2021-06-19 14:57:27 +02:00
2021-07-21 03:17:34 +02:00
for ( auto & item : intsTmp ) {
ints . at ( item . first ) = item . second ;
}
intsTmp . clear ( ) ;
2021-06-19 14:57:27 +02:00
2021-07-21 03:17:34 +02:00
for ( auto & item : boolsTmp ) {
bools . at ( item . first ) = item . second ;
}
boolsTmp . clear ( ) ;
}
catch ( const std : : exception & e ) {
Global : : exit_error_msg = " Exception during Config::unlock() : " + ( string ) e . what ( ) ;
clean_quit ( 1 ) ;
2021-06-19 14:57:27 +02:00
}
locked = false ;
writelock = false ;
2021-07-18 15:44:32 +02:00
writelock . notify_all ( ) ;
2021-06-19 14:57:27 +02:00
}
2021-07-21 03:17:34 +02:00
bool check_boxes ( string boxes ) {
2021-07-18 15:44:32 +02:00
auto new_boxes = ssplit ( boxes ) ;
for ( auto & box : new_boxes ) {
2021-07-04 01:18:48 +02:00
if ( not v_contains ( valid_boxes , box ) ) return false ;
}
2021-07-18 15:44:32 +02:00
current_boxes . swap ( new_boxes ) ;
2021-07-04 01:18:48 +02:00
return true ;
}
2021-07-21 03:17:34 +02:00
void load ( fs : : path conf_file , vector < string > & load_errors ) {
2021-06-19 14:57:27 +02:00
if ( conf_file . empty ( ) )
return ;
2021-06-21 22:52:55 +02:00
else if ( not fs : : exists ( conf_file ) ) {
2021-06-19 14:57:27 +02:00
write_new = true ;
return ;
}
std : : ifstream cread ( conf_file ) ;
if ( cread . good ( ) ) {
vector < string > valid_names ;
for ( auto & n : descriptions )
valid_names . push_back ( n [ 0 ] ) ;
string v_string ;
getline ( cread , v_string , ' \n ' ) ;
2021-06-21 22:52:55 +02:00
if ( not v_string . ends_with ( Global : : Version ) )
2021-06-19 14:57:27 +02:00
write_new = true ;
2021-06-21 22:52:55 +02:00
while ( not cread . eof ( ) ) {
2021-06-19 14:57:27 +02:00
cread > > std : : ws ;
if ( cread . peek ( ) = = ' # ' ) {
cread . ignore ( SSmax , ' \n ' ) ;
continue ;
}
string name , value ;
getline ( cread , name , ' = ' ) ;
2021-07-18 15:44:32 +02:00
if ( name . ends_with ( ' ' ) ) name = trim ( name ) ;
2021-06-21 22:52:55 +02:00
if ( not v_contains ( valid_names , name ) ) {
2021-06-19 14:57:27 +02:00
cread . ignore ( SSmax , ' \n ' ) ;
continue ;
}
2021-07-18 15:44:32 +02:00
cread > > std : : ws ;
2021-06-19 14:57:27 +02:00
if ( bools . contains ( name ) ) {
cread > > value ;
2021-06-21 22:52:55 +02:00
if ( not isbool ( value ) )
2021-06-19 14:57:27 +02:00
load_errors . push_back ( " Got an invalid bool value for config name: " + name ) ;
else
bools . at ( name ) = stobool ( value ) ;
}
else if ( ints . contains ( name ) ) {
cread > > value ;
2021-06-21 22:52:55 +02:00
if ( not isint ( value ) )
2021-06-19 14:57:27 +02:00
load_errors . push_back ( " Got an invalid integer value for config name: " + name ) ;
else
ints . at ( name ) = stoi ( value ) ;
}
else if ( strings . contains ( name ) ) {
if ( cread . peek ( ) = = ' " ' ) {
cread . ignore ( 1 ) ;
getline ( cread , value , ' " ' ) ;
}
else cread > > value ;
2021-06-21 22:52:55 +02:00
if ( name = = " log_level " and not v_contains ( Logger : : log_levels , value ) )
2021-06-19 14:57:27 +02:00
load_errors . push_back ( " Invalid log_level: " + value ) ;
2021-06-21 22:52:55 +02:00
else if ( name = = " graph_symbol " and not v_contains ( valid_graph_symbols , value ) )
2021-06-19 14:57:27 +02:00
load_errors . push_back ( " Invalid graph symbol identifier: " + value ) ;
2021-07-04 01:18:48 +02:00
else if ( name = = " shown_boxes " and not value . empty ( ) and not check_boxes ( value ) )
load_errors . push_back ( " Invalid box name in shown_boxes. Using default. " ) ;
2021-06-19 14:57:27 +02:00
else
strings . at ( name ) = value ;
}
cread . ignore ( SSmax , ' \n ' ) ;
}
cread . close ( ) ;
2021-06-21 22:52:55 +02:00
if ( not load_errors . empty ( ) ) write_new = true ;
2021-06-19 14:57:27 +02:00
}
}
2021-07-21 03:17:34 +02:00
void write ( ) {
2021-06-21 22:52:55 +02:00
if ( conf_file . empty ( ) or not write_new ) return ;
2021-06-19 14:57:27 +02:00
Logger : : debug ( " Writing new config file " ) ;
std : : ofstream cwrite ( conf_file , std : : ios : : trunc ) ;
if ( cwrite . good ( ) ) {
cwrite < < " #? Config file for btop v. " < < Global : : Version ;
for ( auto [ name , description ] : descriptions ) {
2021-07-18 15:44:32 +02:00
cwrite < < " \n \n " < < ( description . empty ( ) ? " " : description + " \n " )
< < name < < " = " ;
if ( strings . contains ( name ) )
cwrite < < " \" " < < strings . at ( name ) < < " \" " ;
else if ( ints . contains ( name ) )
cwrite < < ints . at ( name ) ;
else if ( bools . contains ( name ) )
cwrite < < ( bools . at ( name ) ? " True " : " False " ) ;
2021-06-19 14:57:27 +02:00
}
cwrite . close ( ) ;
}
}
}