diff --git a/Makefile b/Makefile index 3cf8d31..f2dc173 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,21 @@ PREFIX ?= /usr/local DOCDIR ?= $(PREFIX)/share/btop/doc #Compiler and Linker -CXX := g++ +CXX := g++ + +#If using g++ try to make sure we are using version 11 or 10 +ifeq ($(CXX),g++) + CXX_VERSION = $(shell $(CXX) -dumpversion) + ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0) + ifneq ($(shell command -v g++-11),) + CXX := g++-11 + else ifneq ($(shell test $(CXX_VERSION) -eq 10; echo $$?),0) + ifneq ($(shell command -v g++-10),) + CXX := g++-10 + endif + endif + endif +endif #The Target Binary Program TARGET := btop @@ -20,9 +34,6 @@ OBJEXT := o CXXFLAGS := -std=c++20 -pthread -O3 -Wall -Wextra -Wno-stringop-overread -pedantic INC := -I$(INCDIR) -I$(SRCDIR) -#--------------------------------------------------------------------------------- -#DO NOT EDIT BELOW THIS LINE -#--------------------------------------------------------------------------------- SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT))) @@ -34,12 +45,12 @@ directories: @mkdir -p $(TARGETDIR) @mkdir -p $(BUILDDIR) -#Clean only Objecst +#Clean only Objects clean: @rm -rf $(BUILDDIR) #Full Clean, Objects and Binaries -dist-clean: clean +distclean: clean @rm -rf $(TARGETDIR) install: @@ -50,6 +61,14 @@ install: @cp -pr themes $(DESTDIR)$(PREFIX)/share/btop @chmod 755 $(DESTDIR)$(PREFIX)/bin/btop +#Set suid bit for btop to root, will make btop run as root regardless of actual user +su-setuid: + @su --session-command "make as-root-setuid" root + +as-root-setuid: + @chown root:root $(DESTDIR)$(PREFIX)/bin/btop + @chmod 4755 $(DESTDIR)$(PREFIX)/bin/btop + uninstall: @rm -rf $(DESTDIR)$(PREFIX)/bin/btop @rm -rf $(DESTDIR)$(DOCDIR) diff --git a/README.md b/README.md index 50e8026..581c4bd 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ * [Themes](#themes) * [Support and funding](#support-and-funding) * [Prerequisites](#prerequisites) (Read this if you are having issues!) -* [Dependencies](#dependencies) * [Screenshots](#screenshots) * [Installation](#installation) * [Configurability](#configurability) @@ -67,7 +66,7 @@ Btop++ uses the same theme files as bpytop and bashtop (some color values missin See [themes](https://github.com/aristocratos/btop/tree/master/themes) folder for available themes. -The `make install` command places the default themes in `[/usr/local]/share/btop/themes`. +The `make install` command places the default themes in `[$PREFIX or /usr/local]/share/btop/themes`. User created themes should be placed in `$XDG_CONFIG_HOME/btop/themes` or `$HOME/.config/btop/themes`. Let me know if you want to contribute with new themes. @@ -82,15 +81,16 @@ Any support is greatly appreciated! ## Prerequisites -For correct display, a terminal with support for: +For best experience, a terminal with support for: * 24-bit truecolor ([See list of terminals with truecolor support](https://gist.github.com/XVilka/8346728)) * 256-color terminals are supported through 24-bit to 256-color conversion when setting "truecolor" to False in the options or with "-lc/--low-color" argument. + (16 color TTY mode now available as well.) * Wide characters (Are sometimes problematic in web-based terminals) Also needs a UTF8 locale and a font that covers: -* Unicode Block “Braille Patterns” U+2800 - U+28FF +* Unicode Block “Braille Patterns” U+2800 - U+28FF (Not needed in TTY mode or with graphs set to type: block or tty.) * Unicode Block “Geometric Shapes” U+25A0 - U+25FF * Unicode Block "Box Drawing" and "Block Elements" U+2500 - U+259F @@ -108,14 +108,6 @@ If text are misaligned and you are using Konsole or Yakuake, turning off "Bi-Dir Characters clipping in to each other or text/border misalignments is not bugs caused by bpytop, but most likely a fontconfig or terminal problem where the braille characters making up the graphs aren't rendered correctly. Look to the creators of the terminal emulator you use to fix these issues if the previous mentioned fixes don't work for you. -## Dependencies - -None - -### Compiling from source - -Needs at least G++ 10, preferably 11 (or higher) to make use of some C++20 functionality. - ## Screenshots Main UI showing details for a selected process. @@ -134,7 +126,14 @@ Options menu. #### Manual compilation and installation -Requires GCC/G++ 10 or higher! +Needs at least GCC/G++ 10, preferably 11 (or higher) to make use of some C++20 functionality. + +>Install dependencies (Ubuntu 21.04 Hirsute) + +``` bash +sudo apt install git build-essential gcc-11 g++-11 + # On earlier Ubuntu versions that don't have gcc-11 and g++-11 replace with gcc-10 and g++-10 +``` >Clone and compile @@ -142,26 +141,42 @@ Requires GCC/G++ 10 or higher! git clone https://github.com/aristocratos/btop.git cd btop make + # use "make -jX" where X is your number of cores for multithreaded compilation ``` >to install ``` bash sudo make install + # use "make install PREFIX=/target/dir" to set target, default: /usr/local + # only use "sudo" when installing to a NON user owned directory ``` +>to make btop always run as root (to enable signal sending to any process and for /proc read permissions on some systems) + +``` bash +make su-setuid +``` + + >to uninstall ``` bash sudo make uninstall ``` ->to remove any compiled files +>to remove any object files ```bash make clean ``` +>to remove all object files, binaries and created directories + +```bash +make distclean +``` + ## Configurability All options changeable from within UI. diff --git a/src/btop.cpp b/src/btop.cpp index 513205a..b1df9a1 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -599,7 +599,7 @@ int main(int argc, char **argv){ size_t lc; string ostring; uint64_t tsl, timestamp2, rcount = 0; - list avgtimes = {0}; + list avgtimes; size_t timer = 2000; bool filtering = false; vector greyscale; @@ -657,8 +657,8 @@ int main(int argc, char **argv){ - if (rcount > 0) avgtimes.push_front(timestamp); - if (avgtimes.size() > 100) avgtimes.pop_back(); + avgtimes.push_front(timestamp); + if (avgtimes.size() > 30) avgtimes.pop_back(); cout << pbox << ostring << Fx::reset << "\n" << endl; cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 5) << " μs. Average: " << rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() << diff --git a/src/btop_config.cpp b/src/btop_config.cpp index e97bbe5..0ed0864 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -211,12 +211,14 @@ namespace Config { {"tty_mode", false}, {"force_tty", false}, {"lowcolor", false}, + {"show_detailed", false}, }; unordered_flat_map boolsTmp; unordered_flat_map ints = { {"update_ms", 2000}, {"proc_update_mult", 2}, + {"detailed_pid", 0}, }; unordered_flat_map intsTmp; @@ -273,6 +275,7 @@ namespace Config { } void unlock(){ + if (not locked) return; atomic_wait_set(writelock); for (auto& item : stringsTmp){ diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp index 2459f92..bea804e 100644 --- a/src/btop_draw.cpp +++ b/src/btop_draw.cpp @@ -106,25 +106,25 @@ namespace Draw { out = Fx::reset + lcolor; - //* Draw horizontal lines + //? Draw horizontal lines for (size_t hpos : {c.y, c.y + c.height - 1}){ out += Mv::to(hpos, c.x) + Symbols::h_line * (c.width - 1); } - //* Draw vertical lines and fill if enabled + //? Draw vertical lines and fill if enabled for (size_t hpos : iota(c.y + 1, c.y + c.height - 1)){ out += Mv::to(hpos, c.x) + Symbols::v_line + ((c.fill) ? string(c.width - 2, ' ') : Mv::r(c.width - 2)) + Symbols::v_line; } - //* Draw corners + //? Draw corners out += Mv::to(c.y, c.x) + Symbols::left_up + Mv::to(c.y, c.x + c.width - 1) + Symbols::right_up + Mv::to(c.y + c.height - 1, c.x) + Symbols::left_down + Mv::to(c.y + c.height - 1, c.x + c.width - 1) + Symbols::right_down; - //* Draw titles if defined + //? Draw titles if defined if (not c.title.empty()){ out += Mv::to(c.y, c.x + 2) + Symbols::title_left + Fx::b + numbering + Theme::c("title") + c.title + Fx::ub + lcolor + Symbols::title_right; @@ -199,7 +199,7 @@ namespace Draw { if (no_zero and horizon == height - 1 and i != -1 and result[ai] == 0) result[ai] = 1; } } - //? Generate braille symbol from 5x5 2D vector + //? Generate graph symbol from 5x5 2D vector graphs[current][horizon] += (height == 1 and result[0] + result[1] == 0) ? Mv::r(1) : graph_symbol[(result[0] * 5 + result[1])]; } if (mult and i > data_offset) last = data_value; diff --git a/src/btop_linux.cpp b/src/btop_linux.cpp index dc6d5d8..fbcc048 100644 --- a/src/btop_linux.cpp +++ b/src/btop_linux.cpp @@ -95,6 +95,7 @@ namespace Proc { namespace { struct p_cache { string name, cmd, user; + size_t name_offset; uint64_t cpu_t = 0, cpu_s = 0; string prefix = ""; size_t depth = 0; @@ -120,8 +121,10 @@ namespace Proc { "cpu lazy", }; + detail_container detailed; + //* Generate process tree list - void _tree_gen(const proc_info& cur_proc, const vector& in_procs, vector& out_procs, int cur_depth, const bool collapsed, const string& prefix, const string& filter, bool found){ + void _tree_gen(const proc_info& cur_proc, const vector& in_procs, vector& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false){ auto cur_pos = out_procs.size(); bool filtering = false; @@ -150,14 +153,46 @@ namespace Proc { out_procs.back().threads += p.threads; } else children++; - _tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cache.at(cur_proc.pid).collapsed), prefix, filter, found); + _tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cache.at(cur_proc.pid).collapsed), filter, found); } if (collapsed or filtering) return; if (out_procs.size() > cur_pos + 1 and not out_procs.back().prefix.ends_with("] ")) out_procs.back().prefix.replace(out_procs.back().prefix.size() - 8, 8, " └─ "); - out_procs.at(cur_pos).prefix = " │ "s * cur_depth + (children > 0 ? (cache.at(cur_proc.pid).collapsed ? "[+] " : "[-] ") : prefix); + out_procs.at(cur_pos).prefix = " │ "s * cur_depth + (children > 0 ? (cache.at(cur_proc.pid).collapsed ? "[+] " : "[-] ") : " ├─ "); + } + + //* Get datiled info for selected process + void _collect_details(proc_info p){ + fs::path pid_path = Shared::proc_path / std::to_string(p.pid); + + detailed.entry = p; + ifstream d_read; + + //* Get RSS mem from smaps + if (fs::exists(pid_path / "smaps")) { + pid_path /= "smaps"; + d_read.open(pid_path); + if (d_read.good()) { + uint64_t rss = 0; + string val; + try { + while (not d_read.eof()) { + d_read.ignore(SSmax, 'R'); + if (d_read.peek() == 's') { + d_read.ignore(SSmax, ':'); + getline(d_read, val, 'k'); + rss += stoull(val) << 10; + } + } + detailed.entry.mem = rss; + } + catch (std::invalid_argument const&) {} + catch (std::out_of_range const&) {} + } + d_read.close(); + } } vector current_procs; @@ -166,10 +201,12 @@ namespace Proc { vector& collect(){ atomic_wait_set(collecting); auto& sorting = Config::getS("proc_sorting"); - auto& reverse = Config::getB("proc_reversed"); + auto reverse = Config::getB("proc_reversed"); auto& filter = Config::getS("proc_filter"); - auto& per_core = Config::getB("proc_per_core"); - auto& tree = Config::getB("proc_tree"); + auto per_core = Config::getB("proc_per_core"); + auto tree = Config::getB("proc_tree"); + auto show_detailed = Config::getB("show_detailed"); + size_t detailed_pid = Config::getI("detailed_pid"); ifstream pread; string long_string; string short_str; @@ -178,6 +215,7 @@ namespace Proc { procs.reserve((numpids + 10)); int npids = 0; int cmult = (per_core) ? Global::coreCount : 1; + bool got_detailed = false; //* Update uid_user map if /etc/passwd changed since last run if (not Shared::passwd_path.empty() and fs::last_write_time(Shared::passwd_path) != Shared::passwd_time) { @@ -218,154 +256,160 @@ namespace Proc { bool new_cache = false; string pid_str = d.path().filename(); - if (d.is_directory() and isdigit(pid_str[0])) { - npids++; - proc_info new_proc (stoul(pid_str)); + if (not isdigit(pid_str[0])) continue; - //* Cache program name, command and username - if (not cache.contains(new_proc.pid)) { - string name, cmd, user; - new_cache = true; - pread.open(d.path() / "comm"); - if (pread.good()) { - getline(pread, name); - pread.close(); - } - else continue; + npids++; + proc_info new_proc (stoul(pid_str)); - pread.open(d.path() / "cmdline"); - if (pread.good()) { - string tmpstr; - while(getline(pread, tmpstr, '\0')) cmd += tmpstr + ' '; - pread.close(); - if (not cmd.empty()) cmd.pop_back(); - } - else continue; + //* Cache program name, command and username + if (not cache.contains(new_proc.pid)) { + string name, cmd, user; + new_cache = true; + pread.open(d.path() / "comm"); + if (not pread.good()) continue; + getline(pread, name); + pread.close(); + size_t name_offset = rng::count(name, ' '); - pread.open(d.path() / "status"); - if (pread.good()) { - string uid; - while (not pread.eof()){ - string line; - getline(pread, line, ':'); - if (line == "Uid") { - pread.ignore(); - getline(pread, uid, '\t'); - break; - } else { - pread.ignore(SSmax, '\n'); - } - } - pread.close(); - user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid; + + pread.open(d.path() / "cmdline"); + if (not pread.good()) continue; + long_string.clear(); + while(getline(pread, long_string, '\0')) cmd += long_string + ' '; + pread.close(); + if (not cmd.empty()) cmd.pop_back(); + + + pread.open(d.path() / "status"); + if (not pread.good()) continue; + string uid; + string line; + while (not pread.eof()){ + getline(pread, line, ':'); + if (line == "Uid") { + pread.ignore(); + getline(pread, uid, '\t'); + break; + } else { + pread.ignore(SSmax, '\n'); } - else continue; - cache[new_proc.pid] = {name, cmd, user}; } + pread.close(); + user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid; - //* Match filter if defined - if (not tree and not filter.empty() - and pid_str.find(filter) == string::npos - and cache[new_proc.pid].name.find(filter) == string::npos - and cache[new_proc.pid].cmd.find(filter) == string::npos - and cache[new_proc.pid].user.find(filter) == string::npos) { - if (new_cache) cache.erase(new_proc.pid); - continue; - } - new_proc.name = cache[new_proc.pid].name; - new_proc.cmd = cache[new_proc.pid].cmd; - new_proc.user = cache[new_proc.pid].user; - - //* Parse /proc/[pid]/stat - pread.open(d.path() / "stat"); - if (pread.good()) { - - //? Check cached name for whitespace characters and set offset to get correct fields from stat file - size_t offset = rng::count(cache.at(new_proc.pid).name, ' '); - size_t x = 0, next_x = 3; - uint64_t cpu_t = 0; - try { - while (not pread.eof()) { - if (++x > 40 + offset) break; - - if (x-offset < next_x) { - pread.ignore(SSmax, ' '); - continue; - } - else - getline(pread, short_str, ' '); - - switch (x-offset) { - case 3: { //? Process state - new_proc.state = short_str[0]; - break; - } - case 4: { //? Process parent pid - new_proc.ppid = stoull(short_str); - next_x = 14; - break; - } - case 14: { //? Process utime - cpu_t = stoull(short_str); - break; - } - case 15: { //? Process stime - cpu_t += stoull(short_str); - next_x = 19; - break; - } - case 19: { //? Process nice value - new_proc.p_nice = stoull(short_str); - break; - } - case 20: { //? Process number of threads - new_proc.threads = stoull(short_str); - next_x = (new_cache) ? 22 : 40; - break; - } - case 22: { //? Cache cpu seconds - cache[new_proc.pid].cpu_s = stoull(short_str); - next_x = 40; - break; - } - case 40: { //? CPU number last executed on - new_proc.cpu_n = stoull(short_str); - break; - } - } - } - pread.close(); - - } - catch (...) { - continue; - } - - if (x-offset < 22) continue; - - //? Process cpu usage since last update - new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache[new_proc.pid].cpu_t) / (cputimes - old_cputimes)) / 10.0; - - //? Process cumulative cpu usage since process start - new_proc.cpu_c = ((double)cpu_t / Shared::clk_tck) / (uptime - (cache[new_proc.pid].cpu_s / Shared::clk_tck)); - - //? Update cache with latest cpu times - cache[new_proc.pid].cpu_t = cpu_t; - } - else continue; - - //* Get RSS memory in bytes from /proc/[pid]/statm - pread.open(d.path() / "statm"); - if (pread.good()) { - pread.ignore(SSmax, ' '); - getline(pread, short_str, ' '); - pread.close(); - new_proc.mem = stoull(short_str) * Shared::page_size; - } - - //? Push process to vector - procs.push_back(new_proc); + cache[new_proc.pid] = {name, cmd, user, name_offset}; } + + //* Match filter if defined + if (not tree and not filter.empty() + and not (show_detailed and new_proc.pid == detailed_pid) + and pid_str.find(filter) == string::npos + and cache[new_proc.pid].name.find(filter) == string::npos + and cache[new_proc.pid].cmd.find(filter) == string::npos + and cache[new_proc.pid].user.find(filter) == string::npos) { + if (new_cache) cache.erase(new_proc.pid); + continue; + } + new_proc.name = cache[new_proc.pid].name; + new_proc.cmd = cache[new_proc.pid].cmd; + new_proc.user = cache[new_proc.pid].user; + + //* Parse /proc/[pid]/stat + pread.open(d.path() / "stat"); + if (not pread.good()) continue; + + //? Check cached value for whitespace characters in name and set offset to get correct fields from stat file + size_t& offset = cache.at(new_proc.pid).name_offset; + short_str.clear(); + size_t x = 0, next_x = 3; + uint64_t cpu_t = 0; + try { + for (;;) { + while (++x - offset < next_x) { + pread.ignore(SSmax, ' '); + } + + getline(pread, short_str, ' '); + + switch (x-offset) { + case 3: { //? Process state + new_proc.state = short_str[0]; + continue; + } + case 4: { //? Parent pid + new_proc.ppid = stoull(short_str); + next_x = 14; + continue; + } + case 14: { //? Process utime + cpu_t = stoull(short_str); + continue; + } + case 15: { //? Process stime + cpu_t += stoull(short_str); + next_x = 19; + continue; + } + case 19: { //? Nice value + new_proc.p_nice = stoull(short_str); + continue; + } + case 20: { //? Number of threads + new_proc.threads = stoull(short_str); + next_x = (new_cache) ? 22 : 24; + continue; + } + case 22: { //? Save cpu seconds to cache if missing + cache[new_proc.pid].cpu_s = stoull(short_str); + next_x = 24; + continue; + } + case 24: { //? RSS memory (can be inaccurate, but parsing smaps increases total cpu usage by ~ 20x) + new_proc.mem = stoull(short_str) * Shared::page_size; + next_x = 40; + continue; + } + case 40: { //? CPU number last executed on + new_proc.cpu_n = stoull(short_str); + goto stat_loop_done; + } + } + } + + } + catch (std::invalid_argument const&) { continue; } + catch (std::out_of_range const&) { continue; } + catch (std::ios_base::failure const&) {} + + stat_loop_done: + pread.close(); + + if (x-offset < 24) continue; + + // _parse_smaps(new_proc); + + //? Process cpu usage since last update + new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache[new_proc.pid].cpu_t) / (cputimes - old_cputimes)) / 10.0; + + //? Process cumulative cpu usage since process start + new_proc.cpu_c = ((double)cpu_t / Shared::clk_tck) / (uptime - (cache[new_proc.pid].cpu_s / Shared::clk_tck)); + + //? Update cache with latest cpu times + cache[new_proc.pid].cpu_t = cpu_t; + + //? Update the details info box for process if active + if (show_detailed and new_proc.pid == detailed_pid) { + _collect_details(new_proc); + got_detailed = true; + } + + //? Push process to vector + procs.push_back(new_proc); + + } + + if (show_detailed and not got_detailed) { + detailed.entry.state = 'X'; } //* Sort processes @@ -403,10 +447,9 @@ namespace Proc { //? Stable sort to retain selected sorting among processes with the same parent rng::stable_sort(procs, rng::less{}, &proc_info::ppid); - string prefix = " ├─ "; //? Start recursive iteration over processes with the lowest shared parent pids for (auto& p : rng::equal_range(procs, procs.at(0).ppid, rng::less{}, &proc_info::ppid)) { - _tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, prefix, filter, false); + _tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, filter); } procs.swap(tree_procs); } diff --git a/src/btop_shared b/src/btop_shared deleted file mode 100644 index 152aa8d..0000000 Binary files a/src/btop_shared and /dev/null differ diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp index 3b4e5d0..94b83b0 100644 --- a/src/btop_shared.hpp +++ b/src/btop_shared.hpp @@ -32,7 +32,7 @@ namespace Global { } namespace Tools { - //* Platform specific function for system_uptime + //* Platform specific function for system_uptime (seconds since last restart) double system_uptime(); } @@ -45,6 +45,8 @@ namespace Proc { extern size_t numpids; extern std::atomic stop; extern std::atomic collecting; + + //? Contains the valid sorting options for processes extern vector sort_vector; //* Container for process information @@ -60,6 +62,14 @@ namespace Proc { string prefix = ""; }; + //* Container for process info box + struct detail_container { + proc_info entry; + string elapsed, parent, status, io_read, io_write; + }; + + extern detail_container detailed; + extern vector current_procs; //* Collects and sorts process information from /proc, saves and returns reference to Proc::current_procs; diff --git a/src/btop_tools.cpp b/src/btop_tools.cpp index 2e50b5a..6d7195b 100644 --- a/src/btop_tools.cpp +++ b/src/btop_tools.cpp @@ -362,12 +362,12 @@ namespace Tools { #if (__GNUC__ > 10) //* Redirects to atomic wait - void atomic_wait(atomic& atom, bool val){ + void atomic_wait(const atomic& atom, bool val){ atom.wait(val); } #else //* Crude implementation of atomic wait for GCC 10 - void atomic_wait(atomic& atom, bool val){ + void atomic_wait(const atomic& atom, bool val){ while (atom == val) std::this_thread::sleep_for(std::chrono::microseconds(1)); } #endif diff --git a/src/btop_tools.hpp b/src/btop_tools.hpp index 65faf20..8a77bf3 100644 --- a/src/btop_tools.hpp +++ b/src/btop_tools.hpp @@ -225,7 +225,7 @@ namespace Tools { string strf_time(string strf); //* Waits for to not be - void atomic_wait(std::atomic& atom, bool val=true); + void atomic_wait(const std::atomic& atom, bool val=true); //* Waits for to not be and then sets it to again void atomic_wait_set(std::atomic& atom, bool val=true);