diff --git a/src/cui.cpp b/src/cui.cpp index 8ce50fd..700401f 100644 --- a/src/cui.cpp +++ b/src/cui.cpp @@ -29,6 +29,11 @@ #include #include +#include +#include +#include +#include + #include "nethogs.h" #include "process.h" #include @@ -61,10 +66,13 @@ const int MIN_COLUMN_WIDTH_DEV = 5; const int COLUMN_WIDTH_SENT = 11; const int COLUMN_WIDTH_RECEIVED = 11; const int COLUMN_WIDTH_UNIT = 6; +const int NUMBER_OF_DECIMALS_SENT = 1; +const int NUMBER_OF_DECIMALS_RECEIVED = 1; const char *COLUMN_FORMAT_PID = "%7d"; -const char *COLUMN_FORMAT_SENT = "%11.3f"; -const char *COLUMN_FORMAT_RECEIVED = "%11.3f"; + +const char *COLUMN_FORMAT_SENT = "%s"; +const char *COLUMN_FORMAT_RECEIVED = "%s"; // All descriptions are padded to 6 characters in length with spaces const char *const desc_view_mode[VIEWMODE_COUNT] = { @@ -72,6 +80,8 @@ const char *const desc_view_mode[VIEWMODE_COUNT] = { constexpr char FILE_SEPARATOR = '/'; +std::string prettyFloat(double val, int decimals, int maxWidth); + class Line { public: Line(const char *name, const char *cmdline, double n_recv_value, @@ -218,9 +228,15 @@ void Line::show(int row, unsigned int proglen, unsigned int devlen) { mvaddstr(row, column_offset_dev, devicename); - mvprintw(row, column_offset_sent, COLUMN_FORMAT_SENT, sent_value); + mvprintw(row, column_offset_sent, COLUMN_FORMAT_SENT, + prettyFloat(sent_value, NUMBER_OF_DECIMALS_SENT, COLUMN_WIDTH_SENT) + .c_str()); + + mvprintw(row, column_offset_received, COLUMN_FORMAT_RECEIVED, + prettyFloat(recv_value, NUMBER_OF_DECIMALS_RECEIVED, + COLUMN_WIDTH_RECEIVED) + .c_str()); - mvprintw(row, column_offset_received, COLUMN_FORMAT_RECEIVED, recv_value); mvaddstr(row, column_offset_unit, desc_view_mode[viewMode]); } @@ -336,9 +352,9 @@ void show_trace(Line *lines[], int nproc) { } /* print the 'unknown' connections, for debugging */ - for (auto it = unknowntcp->connections.begin(); it != unknowntcp->connections.end(); ++it) { - std::cout << "Unknown connection: " - << (*it)->refpacket->gethashstring() + for (auto it = unknowntcp->connections.begin(); + it != unknowntcp->connections.end(); ++it) { + std::cout << "Unknown connection: " << (*it)->refpacket->gethashstring() << std::endl; } } @@ -387,8 +403,14 @@ void show_ncurses(Line *lines[], int nproc) { } attron(A_REVERSE); int totalrow = std::min(rows - 1, 3 + 1 + i); - mvprintw(totalrow, 0, " TOTAL %-*.*s %-*.*s %11.3f %11.3f ", - proglen, proglen, "", devlen, devlen, "", sent_global, recv_global); + mvprintw(totalrow, 0, " TOTAL %-*.*s %-*.*s %s %s ", proglen, + proglen, "", devlen, devlen, "", + prettyFloat(sent_global, NUMBER_OF_DECIMALS_SENT, COLUMN_WIDTH_SENT) + .c_str(), + prettyFloat(recv_global, NUMBER_OF_DECIMALS_RECEIVED, + COLUMN_WIDTH_RECEIVED) + .c_str()); + mvprintw(3 + 1 + i, cols - COLUMN_WIDTH_UNIT, "%s", desc_view_mode[viewMode]); attroff(A_REVERSE); mvprintw(totalrow + 1, 0, "%s", ""); @@ -461,3 +483,43 @@ void do_refresh() { if (refreshlimit != 0 && refreshcount >= refreshlimit) quit_cb(0); } + +// . Returns a String with a nice representation of the floating point value +// 'val' . There will be 'decimals' number of digits after the decimal point . +// There will be thousand separators as "'", independent of the user locale . +// String will have width 'maxWidth', the value is aligned to the right, the +// padding +// values at the left are white spaces. +// . If the the resulting number-string is bigger in than 'maxWidth' a +// default string "ERR:tooBig" is returned, padded to maxWidth. +// . test with something like: +// std::cout << "Test di prettyFloat: " << prettyFloat(123456.123456, 1, 15) +// << "\n"; +// . Adapted from here: https://stackoverflow.com/a/43482688/2129178 +std::string prettyFloat(double val, int decimals, int maxWidth) { + + struct separate_thousands : std::numpunct { + char_type do_thousands_sep() const override { + return ','; + } // separate with commas + string_type do_grouping() const override { + return "\3"; + } // groups of 3 digit + }; + + std::stringstream ss1; + auto thousands = std::make_unique(); + ss1.imbue(std::locale(std::cout.getloc(), thousands.release())); + ss1.setf(std::ios_base::fixed, std::ios_base::floatfield); + ss1.precision(decimals); + ss1 << std::setw(maxWidth); + ss1 << val; + std::string out = ss1.str(); + if (out.length() > (long unsigned)maxWidth) { + ss1.str(""); + ss1 << std::setw(maxWidth); + ss1 << "ERR:tooBig"; + out = ss1.str(); + } + return out; +}; diff --git a/src/libnethogs.cpp b/src/libnethogs.cpp index ef4df82..2d02d05 100644 --- a/src/libnethogs.cpp +++ b/src/libnethogs.cpp @@ -277,11 +277,12 @@ static void nethogsmonitor_clean_up() { } int nethogsmonitor_loop(NethogsMonitorCallback cb, char *filter, int to_ms) { - return nethogsmonitor_loop_devices(cb, filter, 0, NULL, false, to_ms); + return nethogsmonitor_loop_devices(cb, filter, 0, NULL, false, to_ms); } int nethogsmonitor_loop_devices(NethogsMonitorCallback cb, char *filter, - int devc, char **devicenames, bool all, int to_ms) { + int devc, char **devicenames, bool all, + int to_ms) { if (monitor_run_flag) { return NETHOGS_STATUS_FAILURE; } diff --git a/src/libnethogs.h b/src/libnethogs.h index 81d127d..2ea0167 100644 --- a/src/libnethogs.h +++ b/src/libnethogs.h @@ -62,8 +62,7 @@ typedef void (*NethogsMonitorCallback)(int action, */ NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb, - char *filter, - int to_ms); + char *filter, int to_ms); /** * @brief Enter the process monitoring loop and reports updates using the @@ -87,8 +86,7 @@ NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb, NETHOGS_DSO_VISIBLE int nethogsmonitor_loop_devices(NethogsMonitorCallback cb, char *filter, int devc, char **devicenames, - bool all, - int to_ms); + bool all, int to_ms); /** * @brief Makes the call to nethogsmonitor_loop return. diff --git a/src/main.cpp b/src/main.cpp index 2c71f88..da10cca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -51,7 +51,8 @@ static void help(bool iserror) { output << " -C : capture TCP and UDP.\n"; output << " -g : garbage collection period in number of refresh. " "default is 50.\n"; - output << " -b : Short program name. Displays only the program name.\n"; + output << " -b : Short program name. Displays only the program " + "name.\n"; output << " -f : EXPERIMENTAL: specify string pcap filter (like " "tcpdump)." " This may be removed or changed in a future version.\n"; @@ -150,7 +151,6 @@ int main(int argc, char **argv) { char *filter = NULL; int garbage_collection_period = 50; - int opt; while ((opt = getopt(argc, argv, "Vhxtpsd:v:c:laf:Cbg:P:")) != -1) { switch (opt) { @@ -201,7 +201,7 @@ int main(int argc, char **argv) { garbage_collection_period = (time_t)atoi(optarg); break; case 'P': - pidsToWatch.insert((pid_t) atoi(optarg)); + pidsToWatch.insert((pid_t)atoi(optarg)); break; default: help(true); @@ -287,10 +287,11 @@ int main(int argc, char **argv) { if (nb_devices == nb_failed_devices) { if (geteuid() != 0) - fprintf(stderr, "To run nethogs without being root, you need to enable " - "capabilities on the program (cap_net_admin, cap_net_raw, " - "cap_dac_read_search, cap_sys_ptrace). " - "See the documentation for details.\n"); + fprintf(stderr, + "To run nethogs without being root, you need to enable " + "capabilities on the program (cap_net_admin, cap_net_raw, " + "cap_dac_read_search, cap_sys_ptrace). " + "See the documentation for details.\n"); forceExit(false, "Error opening pcap handlers for all devices.\n"); } diff --git a/src/packet.cpp b/src/packet.cpp index 34b8791..323ae44 100644 --- a/src/packet.cpp +++ b/src/packet.cpp @@ -300,9 +300,8 @@ bool Packet::match(const Packet *other) const { bool Packet::matchSource(const Packet *other) const { return sa_family == other->sa_family && (sport == other->sport) && - (sa_family == AF_INET - ? (sameinaddr(sip, other->sip)) - : (samein6addr(sip6, other->sip6))); + (sa_family == AF_INET ? (sameinaddr(sip, other->sip)) + : (samein6addr(sip6, other->sip6))); } Packet Packet::onlySource() const { @@ -313,7 +312,7 @@ Packet Packet::onlySource() const { return p; } -bool Packet::operator< (const Packet& other) const { +bool Packet::operator<(const Packet &other) const { if (sa_family != other.sa_family) return dir < other.sa_family; /* source address first */ diff --git a/src/packet.h b/src/packet.h index 0fde51b..0eb4cfe 100644 --- a/src/packet.h +++ b/src/packet.h @@ -74,7 +74,7 @@ class Packet { bool matchSource(const Packet *other) const; /* returns a copy with destination information stripped (for comparisons) */ Packet onlySource() const; - bool operator< (const Packet& other) const; + bool operator<(const Packet &other) const; /* returns '1.2.3.4:5-1.2.3.4:6'-style string */ char *gethashstring(); diff --git a/src/process.cpp b/src/process.cpp index 1cfc741..6c6dd30 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -30,12 +30,12 @@ #endif #include #include +#include #include #include #include #include #include -#include #include "conninode.h" #include "inode2prog.h" @@ -81,7 +81,6 @@ float tokbps(u_int64_t bytes) { return (((double)bytes) / PERIOD) / KB; } float tombps(u_int64_t bytes) { return (((double)bytes) / PERIOD) / MB; } float togbps(u_int64_t bytes) { return (((double)bytes) / PERIOD) / GB; } - void process_init() { unknowntcp = new Process(0, "", "unknown TCP"); processes = new ProcList(unknowntcp, NULL); @@ -92,7 +91,6 @@ void process_init() { // unknownip = new Process (0, "", "unknown IP"); // processes = new ProcList (unknownip, processes); } - } int Process::getLastPacket() { @@ -110,7 +108,8 @@ static void sum_active_connections(Process *process_ptr, u_int64_t &sum_sent, u_int64_t &sum_recv) { /* walk though all process_ptr process's connections, and sum * them up */ - for (auto it = process_ptr->connections.begin(); it != process_ptr->connections.end(); ) { + for (auto it = process_ptr->connections.begin(); + it != process_ptr->connections.end();) { if ((*it)->getLastPacket() <= curtime.tv_sec - CONNTIMEOUT) { /* capture sent and received totals before deleting */ process_ptr->sent_by_closed_bytes += (*it)->sumSent; @@ -158,7 +157,8 @@ void Process::getgbps(float *recvd, float *sent) { /** get total values for this process */ void Process::gettotal(u_int64_t *recvd, u_int64_t *sent) { u_int64_t sum_sent = 0, sum_recv = 0; - for (auto it = this->connections.begin(); it != this->connections.end(); ++it) { + for (auto it = this->connections.begin(); it != this->connections.end(); + ++it) { Connection *conn = (*it); sum_sent += conn->sumSent; sum_recv += conn->sumRecv; @@ -253,7 +253,8 @@ Process *getProcess(unsigned long inode, const char *devicename) { if (proc != NULL) return proc; - if ( !(pidsToWatch.empty()) && pidsToWatch.find(node->pid) == pidsToWatch.end() ) { + if (!(pidsToWatch.empty()) && + pidsToWatch.find(node->pid) == pidsToWatch.end()) { return NULL; } diff --git a/src/process.h b/src/process.h index 3730e33..5396b41 100644 --- a/src/process.h +++ b/src/process.h @@ -36,19 +36,19 @@ void check_all_procs(); /* compares Connection pointers by their refpacket */ struct ConnectionComparator { using is_transparent = void; - bool operator()(const Connection* l, const Connection* r) const { + bool operator()(const Connection *l, const Connection *r) const { return *l->refpacket < *r->refpacket; } - bool operator()(const Packet* l, const Connection* r) const { + bool operator()(const Packet *l, const Connection *r) const { return *l < *r->refpacket; } - bool operator()(const Connection* l, const Packet* r) const { + bool operator()(const Connection *l, const Packet *r) const { return *l->refpacket < *r; } }; /* ordered set of Connection pointers */ -typedef std::multiset ConnList; +typedef std::multiset ConnList; class Process { public: