diff --git a/.gitignore b/.gitignore index b2a98ec..2253453 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ libnethogs.a results/ build/ nethogs.egg-info + +# IDE configurations +.vscode +.idea \ No newline at end of file diff --git a/doc/nethogs.8 b/doc/nethogs.8 index e291684..cc9fca9 100644 --- a/doc/nethogs.8 +++ b/doc/nethogs.8 @@ -41,7 +41,7 @@ bughunt mode - implies tracemode. delay for update refresh rate in seconds. default is 1. .TP \fB-v\fP -view mode (0 = kB/s, 1 = total kB, 2 = total bytes, 3 = total MB, 4 = MB/s, 5 = GB/s). default is 0. +view mode (0 = kB/s, 1 = total kB, 2 = total bytes, 3 = total MB, 4 = MB/s, 5 = GB/s, 6 = B/s). default is 0. kB: 2e10 bytes, MB: 2e20 bytes, GB: 2e30 bytes .TP @@ -75,6 +75,12 @@ garbage collection period in number of refresh. default is 50. \fB-P\fP Show only processes with the specified pid(s). .TP +\fB-j\fP +Output in JSON format. +.TP +\fB-z\fP +Sort by PID. +.TP \fB-f\fP EXPERIMENTAL: specify string pcap filter (like tcpdump). This may be removed or changed in a future version. .TP diff --git a/monitor-service/README.md b/monitor-service/README.md new file mode 100644 index 0000000..9424d49 --- /dev/null +++ b/monitor-service/README.md @@ -0,0 +1,28 @@ +# Nethogs monitor + +Use nethogs as a background service to monitor network usage. + +## Installation + +```sh +sh nethogs-monitor-install.sh +``` + +## Usage + +Enable service: +```sh +systemctl enable nethogs-monitor +``` + +Start service: +```sh +systemctl start nethogs-monitor +``` + +View report: +```sh +nethogs-monitor-report.sh +``` + + diff --git a/monitor-service/nethogs-monitor-dashboard-template.html b/monitor-service/nethogs-monitor-dashboard-template.html new file mode 100644 index 0000000..707d414 --- /dev/null +++ b/monitor-service/nethogs-monitor-dashboard-template.html @@ -0,0 +1,1000 @@ + + + + + + Nethogs Network Monitor + + + + +
+
+

🌐 Network Monitor Dashboard

+

+ >>> SYSTEM NETWORK TRAFFIC MONITORING <<< +

+
+ Generated on: GENERATION_TIME_PLACEHOLDER + | Data from last HOURS_BACK_PLACEHOLDER hours + | 0 records +
+
+ +
+
+
-
+
Registered Connections
+
+
+
-
+
Total Download
+
+
+
-
+
Total Upload
+
+
+
-
+
Unique Processes
+
+
+ +
+
+
+ + +
+ +
+ + +
+ + +
+
+ +
+
+
Traffic by Application
+ +
+ +
+
Traffic by Interface
+ +
+ +
+
Traffic Evolution Over Time
+ +
+
+ +
+
+ 📊 Connection Details + 0 records +
+
+ + + + + + + + + + + + + + + + + + +
TimestampProcessPIDUIDInterfaceDownloadUploadTotal
Processing data...
+
+
+
+ + + + \ No newline at end of file diff --git a/monitor-service/nethogs-monitor-install.sh b/monitor-service/nethogs-monitor-install.sh new file mode 100644 index 0000000..2190b9b --- /dev/null +++ b/monitor-service/nethogs-monitor-install.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Create directories +sudo mkdir -p /var/log/nethogs +sudo mkdir -p /opt/nethogs-monitor + +# Move files +sudo cp ../src/nethogs /opt/nethogs-monitor/ +sudo cp nethogs-monitor.sh /opt/nethogs-monitor/ +sudo cp nethogs-monitor-dashboard-template.html /opt/nethogs-monitor/ +sudo cp nethogs-monitor.service /etc/systemd/system/ +sudo cp nethogs-monitor-report.sh /opt/nethogs-monitor/ + +# Set execution permissions +sudo chmod +x /opt/nethogs-monitor/nethogs-monitor.sh +sudo chmod +x /opt/nethogs-monitor/nethogs-monitor-report.sh + +# Link in local bins +sudo ln -sf /opt/nethogs-monitor/nethogs-monitor-report.sh /usr/local/bin/nethogs-monitor-report.sh + +# Reload daemons +sudo systemctl daemon-reload diff --git a/monitor-service/nethogs-monitor-report.sh b/monitor-service/nethogs-monitor-report.sh new file mode 100644 index 0000000..f3e2bfd --- /dev/null +++ b/monitor-service/nethogs-monitor-report.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +set -e + +# Configuration +NETHOGS_LOG="/var/log/nethogs/nethogs.jsonl" +TEMPLATE_FILE="/opt/nethogs-monitor/nethogs-monitor-dashboard-template.html" +TEMP_DIR=$(mktemp -d) +HOURS_BACK="${1:-24}" +OUTPUT_FILE="${2:-${PWD}/nethogs-dashboard.html}" +MAX_LINES=10000 + +log() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >&2 +} + +cleanup() { + rm -rf "$TEMP_DIR" +} + +trap cleanup EXIT + +# Function to extract recent data from JSONL +extract_recent_data() { + local hours_back=$1 + local output_file="$TEMP_DIR/recent_data.jsonl" + + if [ ! -f "$NETHOGS_LOG" ]; then + log "Warning: File $NETHOGS_LOG not found" + echo "[]" > "$output_file" + return + fi + + # Calculate timestamp limit + local time_limit=$(date -d "$hours_back hours ago" +%s) + + # Extract last lines and filter by time + echo "[" > "$output_file" + tail -n $MAX_LINES "$NETHOGS_LOG" | while IFS= read -r line; do + if [ -n "$line" ]; then + # Extract timestamp and convert to epoch + local timestamp_iso=$(echo "$line" | jq -r '.timestamp // empty' 2>/dev/null || echo "") + if [ -n "$timestamp_iso" ]; then + local timestamp_epoch=$(date -d "$timestamp_iso" +%s 2>/dev/null || echo "0") + if [ "$timestamp_epoch" -ge "$time_limit" ]; then + echo "${line}," + fi + fi + fi + done >> "$output_file" + echo "]" >> "$output_file" + + # If no data, create valid empty file + if [ ! -s "$output_file" ]; then + echo "[]" > "$output_file" + fi +} + +# Function to generate HTML with embedded data +generate_html() { + local data_file="$1" + local output="$2" + + # Verify template exists + if [ ! -f "$TEMPLATE_FILE" ]; then + log "Error: Template not found at $TEMPLATE_FILE" + exit 1 + fi + + # Generate generation timestamp + local generation_time=$(date -Iseconds) + + # Copy template and replace placeholders + cp "$TEMPLATE_FILE" "$output" + + #sed -i "s|JSON_DATA_PLACEHOLDER|$json_data|g" "$output" + sed -i "/JSON_DATA_PLACEHOLDER/{ + r $data_file + d + }" "$output" + sed -i "s|GENERATION_TIME_PLACEHOLDER|$generation_time|g" "$output" + sed -i "s|HOURS_BACK_PLACEHOLDER|$HOURS_BACK|g" "$output" + + log "Dashboard HTML generated: $output" +} + +# Main function +main() { + log "Starting dashboard generation" + log "Extracting data from last $HOURS_BACK hours..." + + # Check dependencies + if ! command -v jq &> /dev/null; then + log "Error: jq is not installed. Install with: sudo apt install jq" + exit 1 + fi + + # Extract recent data + extract_recent_data "$HOURS_BACK" + + # Verify data exists + local data_file="$TEMP_DIR/recent_data.jsonl" + local record_count=$(wc -l < "$data_file") + log "Processing $record_count records" + + # Generate HTML + log "Generating dashboard HTML..." + generate_html "$data_file" "$OUTPUT_FILE" + + # Create output directory if it doesn't exist + mkdir -p "$(dirname "$OUTPUT_FILE")" + + log "Dashboard generated successfully: $OUTPUT_FILE" + log "File size: $(du -h "$OUTPUT_FILE" | cut -f1)" + + xdg-open "$OUTPUT_FILE" +} + +# Show help +if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + echo "Usage: $0 [HOURS] [OUTPUT_FILE]" + echo + echo "HOURS: Number of hours to look back for data (default: 24)" + echo "OUTPUT_FILE: Output HTML file (default: ${PWD}/nethogs-dashboard.html)" + echo + echo "Examples:" + echo " $0 # Last 24 hours, default output" + echo " $0 6 # Last 6 hours" + echo " $0 168 /var/www/html/week.html # Last week" + echo + echo "Dependencies: jq" + exit 0 +fi + +# Execute main function +main "$@" \ No newline at end of file diff --git a/monitor-service/nethogs-monitor.service b/monitor-service/nethogs-monitor.service new file mode 100644 index 0000000..1a6e600 --- /dev/null +++ b/monitor-service/nethogs-monitor.service @@ -0,0 +1,31 @@ +[Unit] +Description=Nethogs Network Monitor Service +Documentation=man:nethogs(8) +After=network.target +Wants=network-online.target + +[Service] +Type=simple +User=root +Group=root +ExecStart=/opt/nethogs-monitor/nethogs-monitor.sh +ExecStop=/bin/kill -TERM $MAINPID +PIDFile=/run/nethogs-monitor.pid +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal + +# Configuración de seguridad +NoNewPrivileges=true +ProtectSystem=strict +ReadWritePaths=/var/log/nethogs /run +ProtectHome=true +PrivateTmp=true + +# Límites de recursos +LimitNOFILE=65536 +MemoryMax=512M + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/monitor-service/nethogs-monitor.sh b/monitor-service/nethogs-monitor.sh new file mode 100644 index 0000000..97806d2 --- /dev/null +++ b/monitor-service/nethogs-monitor.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Configuration +LOGFILE="/var/log/nethogs/nethogs.jsonl" +PIDFILE="/run/nethogs-monitor.pid" +NETHOGS_BIN="/opt/nethogs-monitor/nethogs" + +cleanup() { + echo "Stopping nethogs monitor..." + if [ -f "$PIDFILE" ]; then + rm -f "$PIDFILE" + fi + exit 0 +} +trap cleanup SIGTERM SIGINT + +# Write PID +echo $$ > "$PIDFILE" + +# Ensure logfile directory +mkdir -p "$(dirname "$LOGFILE")" + +# Main loop +while true; do + # Execute nethogs + "$NETHOGS_BIN" -j -v 6 -z -C -d 10 >> "$LOGFILE" 2>&1 + + # Si nethogs falla, esperar antes de reintentar + if [ $? -ne 0 ]; then + echo "$(date): Error executing nethogs, retrying in 10 seconds ..." + sleep 10 + fi +done \ No newline at end of file diff --git a/src/connection.cpp b/src/connection.cpp index 0959fa2..060bb81 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -58,6 +58,10 @@ u_int64_t PackList::sumanddel(timeval t) { PackListNode *previous = NULL; while (current != NULL) { + if(current->is_sum == false){ + retval += current->val->len; + current->is_sum = true; + } // std::cout << "Comparing " << current->val->time.tv_sec << " <= " << // t.tv_sec - PERIOD << endl; if (current->val->time.tv_sec <= t.tv_sec - PERIOD) { @@ -68,7 +72,6 @@ u_int64_t PackList::sumanddel(timeval t) { delete current; return retval; } - retval += current->val->len; previous = current; current = current->next; } diff --git a/src/connection.h b/src/connection.h index 1d3b224..c78bd06 100644 --- a/src/connection.h +++ b/src/connection.h @@ -30,6 +30,7 @@ class PackListNode { PackListNode(Packet *m_val, PackListNode *m_next = NULL) { val = m_val; next = m_next; + is_sum = false; } ~PackListNode() { delete val; @@ -38,6 +39,7 @@ class PackListNode { } PackListNode *next; Packet *val; + bool is_sum; }; class PackList { diff --git a/src/cui.cpp b/src/cui.cpp index 137bce6..f8fd0ae 100644 --- a/src/cui.cpp +++ b/src/cui.cpp @@ -44,11 +44,14 @@ extern Process *unknownudp; extern Process *unknownip; extern bool sortRecv; +extern bool sortPID; extern int viewMode; extern bool showcommandline; extern bool showBasename; +extern bool output_json; + extern unsigned refreshlimit; extern unsigned refreshcount; @@ -68,7 +71,7 @@ const char *COLUMN_FORMAT_RECEIVED = "%11.3f"; // All descriptions are padded to 6 characters in length with spaces const char *const desc_view_mode[VIEWMODE_COUNT] = { - "kB/s ", "kB ", "bytes ", "MB ", "MB/s ", "GB/s "}; + "kB/s ", "kB ", "bytes ", "MB ", "MB/s ", "GB/s ", "B/s "}; constexpr char FILE_SEPARATOR = '/'; @@ -90,12 +93,11 @@ class Line { void show(int row, unsigned int proglen, unsigned int devlen); void log(); + void json(); double sent_value; double recv_value; const char *devicename; - -private: const char *m_name; const char *m_cmdline; pid_t m_pid; @@ -232,6 +234,48 @@ void Line::log() { << recv_value << std::endl; } +#include + +std::string escape_json(const std::string &s) { + std::ostringstream o; + for (auto c = s.cbegin(); c != s.cend(); c++) { + switch (*c) { + case '"': o << "\\\""; break; + case '\\': o << "\\\\"; break; + case '\b': o << "\\b"; break; + case '\f': o << "\\f"; break; + case '\n': o << "\\n"; break; + case '\r': o << "\\r"; break; + case '\t': o << "\\t"; break; + default: + if ('\x00' <= *c && *c <= '\x1f') { + o << "\\u" + << std::hex << std::setw(4) << std::setfill('0') << static_cast(*c); + } else { + o << *c; + } + } + } + return o.str(); +} + +void Line::json() { + std::cout << "{"; + std::cout << "\"name\": \"" << escape_json(m_name) << "\""; + std::cout << ", "; + std::cout << "\"pid\": \"" << m_pid << "\""; + std::cout << ", "; + std::cout << "\"uid\": \"" << m_uid << "\""; + std::cout << ", "; + std::cout << "\"devicename\": \"" << devicename << "\""; + std::cout << ", "; + std::cout << "\"sent\": " << sent_value; + std::cout << ", "; + std::cout << "\"recv\": " << recv_value; + std::cout << "}"; +} + + int get_devlen(Line *lines[], int nproc, int rows) { int devlen = MIN_COLUMN_WIDTH_DEV; int curlen; @@ -255,14 +299,18 @@ int GreatestFirst(const void *ma, const void *mb) { Line *a = *pa; Line *b = *pb; double aValue; - if (sortRecv) { + if (sortPID) { + aValue = a->m_pid; + } else if (sortRecv) { aValue = a->recv_value; } else { aValue = a->sent_value; } double bValue; - if (sortRecv) { + if (sortPID) { + bValue = (double)b->m_pid; + } else if (sortRecv) { bValue = b->recv_value; } else { bValue = b->sent_value; @@ -343,6 +391,28 @@ void show_trace(Line *lines[], int nproc) { } } + +char* get_iso8601_timestamp() { + static char buffer[32]; + time_t now = time(NULL); + struct tm *utc = gmtime(&now); + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", utc); + return buffer; +} + +void show_json(Line *lines[], int nproc) { + /* print them */ + std::cout << "{\"timestamp\": \""<< get_iso8601_timestamp() << "\", \"processes\": ["; + for (int i = 0; i < nproc; i++) { + if(i>0){ + std::cout << ","; + } + lines[i]->json(); + delete lines[i]; + } + std::cout << "]}"<< std::endl; +} + void show_ncurses(Line *lines[], int nproc) { int rows; // number of terminal rows int cols; // number of terminal columns @@ -436,6 +506,8 @@ void do_refresh() { curproc->getVal()->gettotalmb(&value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_B) { curproc->getVal()->gettotalb(&value_recv, &value_sent); + } else if (viewMode == VIEWMODE_BPS) { + curproc->getVal()->getbps(&value_recv, &value_sent); } else { forceExit(false, "Invalid viewMode: %d", viewMode); } @@ -453,7 +525,9 @@ void do_refresh() { /* sort the accumulated lines */ qsort(lines, nproc, sizeof(Line *), GreatestFirst); - if (tracemode || DEBUG) + if (output_json) + show_json(lines, nproc); + else if (tracemode || DEBUG) show_trace(lines, nproc); else show_ncurses(lines, nproc); diff --git a/src/decpcap.c b/src/decpcap.c index 37e2fcf..f2ab655 100644 --- a/src/decpcap.c +++ b/src/decpcap.c @@ -35,7 +35,7 @@ bool catchall = false; /* functions to set up a handle (which is basically just a pcap handle) */ -struct dp_handle *dp_fillhandle(pcap_t *phandle) { +struct dp_handle *dp_fillhandle(pcap_t *phandle, bool quiet) { struct dp_handle *retval = (struct dp_handle *)malloc(sizeof(struct dp_handle)); int i; @@ -47,37 +47,39 @@ struct dp_handle *dp_fillhandle(pcap_t *phandle) { retval->linktype = pcap_datalink(retval->pcap_handle); - switch (retval->linktype) { - case (DLT_EN10MB): - fprintf(stdout, "Ethernet link detected\n"); - break; - case (DLT_PPP): - fprintf(stdout, "PPP link detected\n"); - break; - case (DLT_LINUX_SLL): - fprintf(stdout, "Linux Cooked Socket link detected\n"); - break; - default: - fprintf(stdout, "No PPP or Ethernet link: %d\n", retval->linktype); - // TODO maybe error? or 'other' callback? - break; + if((!quiet)){ + switch (retval->linktype) { + case (DLT_EN10MB): + fprintf(stdout, "Ethernet link detected\n"); + break; + case (DLT_PPP): + fprintf(stdout, "PPP link detected\n"); + break; + case (DLT_LINUX_SLL): + fprintf(stdout, "Linux Cooked Socket link detected\n"); + break; + default: + fprintf(stdout, "No PPP or Ethernet link: %d\n", retval->linktype); + // TODO maybe error? or 'other' callback? + break; + } } return retval; } -struct dp_handle *dp_open_offline(char *fname, char *ebuf) { +struct dp_handle *dp_open_offline(char *fname, char *ebuf, bool quiet) { pcap_t *temp = pcap_open_offline(fname, ebuf); if (temp == NULL) { return NULL; } - return dp_fillhandle(temp); + return dp_fillhandle(temp, quiet); } struct dp_handle *dp_open_live(const char *device, int snaplen, int promisc, - int to_ms, char *filter, char *errbuf) { + int to_ms, char *filter, char *errbuf, bool quiet) { struct bpf_program fp; // compiled filter program bpf_u_int32 maskp; // subnet mask bpf_u_int32 netp; // interface IP @@ -107,7 +109,7 @@ struct dp_handle *dp_open_live(const char *device, int snaplen, int promisc, } } - return dp_fillhandle(temp); + return dp_fillhandle(temp, quiet); } /* function to get packet statistics, e.g. dropped packets */ diff --git a/src/decpcap.h b/src/decpcap.h index 6d2c86f..3be4db6 100644 --- a/src/decpcap.h +++ b/src/decpcap.h @@ -67,8 +67,8 @@ struct dp_handle { /* functions to set up a handle (which is basically just a pcap handle) */ struct dp_handle *dp_open_live(const char *device, int snaplen, int promisc, - int to_ms, char *filter, char *errbuf); -struct dp_handle *dp_open_offline(char *fname, char *ebuf); + int to_ms, char *filter, char *errbuf, bool quiet); +struct dp_handle *dp_open_offline(char *fname, char *ebuf, bool quiet); /* function to get packet statistics, e.g. dropped packets */ diff --git a/src/decpcap_test.cpp b/src/decpcap_test.cpp index 5e7923a..3b51269 100644 --- a/src/decpcap_test.cpp +++ b/src/decpcap_test.cpp @@ -39,7 +39,7 @@ int main(int argc, char **argv) { char *errbuf = new char[DP_ERRBUF_SIZE]; - dp_handle *newhandle = dp_open_offline(argv[1], errbuf); + dp_handle *newhandle = dp_open_offline(argv[1], errbuf, false); dp_addcb(newhandle, dp_packet_tcp, process_tcp); int ret = dp_dispatch(newhandle, -1, NULL, 0); if (ret == -1) { diff --git a/src/libnethogs.cpp b/src/libnethogs.cpp index d088090..ee87fae 100644 --- a/src/libnethogs.cpp +++ b/src/libnethogs.cpp @@ -102,7 +102,7 @@ static int nethogsmonitor_init(int devc, char **devicenames, bool all, char errbuf[PCAP_ERRBUF_SIZE]; dp_handle *newhandle = dp_open_live(current_dev->name, BUFSIZ, promiscuous, - to_ms, filter, errbuf); + to_ms, filter, errbuf, false); if (newhandle != NULL) { dp_addcb(newhandle, dp_packet_ip, process_ip); dp_addcb(newhandle, dp_packet_ip6, process_ip6); diff --git a/src/main.cpp b/src/main.cpp index 7501461..a09956e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,7 +39,7 @@ static void help(bool iserror) { output << " -d : delay for update refresh rate in seconds. default " "is 1.\n"; output << " -v : view mode (0 = kB/s, 1 = total kB, 2 = " - "total bytes, 3 = total MB, 4 = MB/s, 5 = GB/s). default is 0.\n"; + "total bytes, 3 = total MB, 4 = MB/s, 5 = GB/s, 6 = B/s). default is 0.\n"; output << " -c : number of updates. default is 0 (unlimited).\n"; output << " -t : tracemode.\n"; // output << " -f : format of packets on interface, default is @@ -69,6 +69,8 @@ static void help(bool iserror) { output << " b: display the program basename instead of the fullpath\n"; output << " m: switch between total (kB, bytes, MB) and throughput (kB/s, " " MB/s, GB/s) mode\n"; + output << " j: json output\n"; + output << " z: sort by PIDn"; } void quit_cb(int /* i */) { @@ -80,7 +82,7 @@ void quit_cb(int /* i */) { } void forceExit(bool success, const char *msg, ...) { - if ((!tracemode) && (!DEBUG)) { + if ((!tracemode) && (!DEBUG) && (!output_json)) { exit_ui(); } @@ -141,7 +143,7 @@ void clean_up() { } procclean(); - if ((!tracemode) && (!DEBUG)) + if ((!tracemode) && (!DEBUG) && (!output_json)) exit_ui(); } @@ -153,7 +155,7 @@ int main(int argc, char **argv) { int garbage_collection_period = 50; int opt; - while ((opt = getopt(argc, argv, "Vhxtpsd:v:c:laf:Cbg:P:")) != -1) { + while ((opt = getopt(argc, argv, "Vhxtpsd:v:c:laf:Cbg:P:jz")) != -1) { switch (opt) { case 'V': versiondisplay(); @@ -204,6 +206,12 @@ int main(int argc, char **argv) { case 'P': pidsToWatch.insert((pid_t)atoi(optarg)); break; + case 'j': + output_json = true; + break; + case 'z': + sortPID = true; + break; default: help(true); exit(EXIT_FAILURE); @@ -246,8 +254,9 @@ int main(int argc, char **argv) { forceExit(false, "getifaddrs failed while establishing local IP."); } + bool quiet = output_json; dp_handle *newhandle = - dp_open_live(current_dev->name, BUFSIZ, promisc, 100, filter, errbuf); + dp_open_live(current_dev->name, BUFSIZ, promisc, 100, filter, errbuf, quiet); if (newhandle != NULL) { dp_addcb(newhandle, dp_packet_ip, process_ip); dp_addcb(newhandle, dp_packet_ip6, process_ip6); @@ -300,7 +309,7 @@ int main(int argc, char **argv) { struct dpargs *userdata = (dpargs *)malloc(sizeof(struct dpargs)); - if ((!tracemode) && (!DEBUG)) { + if ((!tracemode) && (!DEBUG) && (!output_json)) { init_ui(); } @@ -328,7 +337,7 @@ int main(int argc, char **argv) { time_t const now = ::time(NULL); if (last_refresh_time + refreshdelay <= now) { last_refresh_time = now; - if ((!DEBUG) && (!tracemode)) { + if ((!DEBUG) && (!tracemode) && (!output_json)) { // handle user input ui_tick(); } diff --git a/src/nethogs.cpp b/src/nethogs.cpp index 6e5d487..ebab94f 100644 --- a/src/nethogs.cpp +++ b/src/nethogs.cpp @@ -59,10 +59,12 @@ bool tracemode = false; bool bughuntmode = false; // sort on sent or received? bool sortRecv = true; +bool sortPID = false; bool showcommandline = false; bool showBasename = false; // viewMode: kb/s or total int viewMode = VIEWMODE_KBPS; +bool output_json = false; const char version[] = " version " VERSION; timeval curtime; diff --git a/src/nethogs.h b/src/nethogs.h index 3e5829a..dff4477 100644 --- a/src/nethogs.h +++ b/src/nethogs.h @@ -70,6 +70,7 @@ enum { VIEWMODE_TOTAL_MB, VIEWMODE_MBPS, VIEWMODE_GBPS, + VIEWMODE_BPS, VIEWMODE_COUNT }; diff --git a/src/process.cpp b/src/process.cpp index bf61efd..b750590 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -83,10 +83,12 @@ float togbps(u_int64_t bytes) { return (((double)bytes) / PERIOD) / GB; } void process_init() { unknowntcp = new Process(0, "", "unknown TCP"); + unknowntcp->keep = true; processes = new ProcList(unknowntcp, NULL); if (catchall) { unknownudp = new Process(0, "", "unknown UDP"); + unknownudp->keep = true; processes = new ProcList(unknownudp, processes); // unknownip = new Process (0, "", "unknown IP"); // processes = new ProcList (unknownip, processes); @@ -127,6 +129,16 @@ static void sum_active_connections(Process *process_ptr, u_int64_t &sum_sent, } } +/** Get the b/s values for this process */ +void Process::getbps(float *recvd, float *sent) { + u_int64_t sum_sent = 0, sum_recv = 0; + + sum_active_connections(this, sum_sent, sum_recv); + *recvd = sum_recv; + *sent = sum_sent; +} + + /** Get the kb/s values for this process */ void Process::getkbps(float *recvd, float *sent) { u_int64_t sum_sent = 0, sum_recv = 0; @@ -439,4 +451,29 @@ void remove_timed_out_processes() { } } -void garbage_collect_processes() { garbage_collect_inodeproc(); } +void garbage_collect_processes() +{ + garbage_collect_inodeproc(); + + ProcList *previousproc = NULL; + ProcList *curProc = processes; + while (curProc != NULL) { + Process *curProcVal = curProc->getVal(); + if (curProcVal->connections.empty() && curProcVal->keep == false) { + ProcList *toDelete = curProc; + if (previousproc == NULL) { + processes = curProc->next; + }else + { + previousproc->next = curProc->next; + } + curProc = curProc->next; + delete curProcVal; + delete toDelete; + } else { + previousproc = curProc; + curProc = curProc->next; + } + } + +} diff --git a/src/process.h b/src/process.h index f20638a..b23c467 100644 --- a/src/process.h +++ b/src/process.h @@ -79,6 +79,7 @@ class Process { rcvd_by_closed_bytes = 0; sent_last_reported = 0; rcvd_last_reported = 0; + keep = false; } void check() { assert(pid >= 0); } @@ -91,6 +92,7 @@ class Process { int getLastPacket(); void gettotal(u_int64_t *recvd, u_int64_t *sent); + void getbps(float *recvd, float *sent); void getkbps(float *recvd, float *sent); void getmbps(float *recvd, float *sent); void getgbps(float *recvd, float *sent); @@ -116,6 +118,8 @@ class Process { unsigned long getInode() { return inode; } + bool keep; + private: const unsigned long inode; uid_t uid;