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
+
+
+
+
+
+
+
+
+
+
-
+
Registered Connections
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Traffic by Application
+
+
+
+
+
Traffic by Interface
+
+
+
+
+
Traffic Evolution Over Time
+
+
+
+
+
+
+
+
+
+
+ | Timestamp |
+ Process |
+ PID |
+ UID |
+ Interface |
+ Download |
+ Upload |
+ Total |
+
+
+
+
+ | 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;