Compare commits

...

10 Commits

21 changed files with 1444 additions and 40 deletions

4
.gitignore vendored
View File

@@ -10,3 +10,7 @@ libnethogs.a
results/ results/
build/ build/
nethogs.egg-info nethogs.egg-info
# IDE configurations
.vscode
.idea

View File

@@ -81,7 +81,17 @@ If you want to remove Nethogs from your system, you can:
sudo make uninstall sudo make uninstall
### Running without root ### Running
See the manpage for the options.
You will see a line called 'unknown TCP' - this bucket is used for traffic
that cannot be associated with any process, for example because the process
has terminated before its traffic could be counted. As long as there's not
a significant amount of traffic in this bucket it can likely be safely
ignored.
#### Running without root
In order to be run by a non-root user, nethogs needs the `cap_net_admin` and `cap_net_raw` capabilities; additionally, to read and display process names, `cap_dac_read_search` and `cap_sys_ptrace` capabilities are required. These can be set on the executable by using the `setcap` command, as follows: In order to be run by a non-root user, nethogs needs the `cap_net_admin` and `cap_net_raw` capabilities; additionally, to read and display process names, `cap_dac_read_search` and `cap_sys_ptrace` capabilities are required. These can be set on the executable by using the `setcap` command, as follows:

View File

@@ -41,7 +41,7 @@ bughunt mode - implies tracemode.
delay for update refresh rate in seconds. default is 1. delay for update refresh rate in seconds. default is 1.
.TP .TP
\fB-v\fP \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 kB: 2e10 bytes, MB: 2e20 bytes, GB: 2e30 bytes
.TP .TP
@@ -75,6 +75,12 @@ garbage collection period in number of refresh. default is 50.
\fB-P\fP \fB-P\fP
Show only processes with the specified pid(s). Show only processes with the specified pid(s).
.TP .TP
\fB-j\fP
Output in JSON format.
.TP
\fB-z\fP
Sort by PID.
.TP
\fB-f\fP \fB-f\fP
EXPERIMENTAL: specify string pcap filter (like tcpdump). This may be removed or changed in a future version. EXPERIMENTAL: specify string pcap filter (like tcpdump). This may be removed or changed in a future version.
.TP .TP

28
monitor-service/README.md Normal file
View File

@@ -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
```

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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 "$@"

View File

@@ -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

View File

@@ -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

View File

@@ -58,6 +58,10 @@ u_int64_t PackList::sumanddel(timeval t) {
PackListNode *previous = NULL; PackListNode *previous = NULL;
while (current != 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 << " <= " << // std::cout << "Comparing " << current->val->time.tv_sec << " <= " <<
// t.tv_sec - PERIOD << endl; // t.tv_sec - PERIOD << endl;
if (current->val->time.tv_sec <= t.tv_sec - PERIOD) { if (current->val->time.tv_sec <= t.tv_sec - PERIOD) {
@@ -68,7 +72,6 @@ u_int64_t PackList::sumanddel(timeval t) {
delete current; delete current;
return retval; return retval;
} }
retval += current->val->len;
previous = current; previous = current;
current = current->next; current = current->next;
} }

View File

@@ -30,6 +30,7 @@ public:
PackListNode(Packet *m_val, PackListNode *m_next = NULL) { PackListNode(Packet *m_val, PackListNode *m_next = NULL) {
val = m_val; val = m_val;
next = m_next; next = m_next;
is_sum = false;
} }
~PackListNode() { ~PackListNode() {
delete val; delete val;
@@ -38,6 +39,7 @@ public:
} }
PackListNode *next; PackListNode *next;
Packet *val; Packet *val;
bool is_sum;
}; };
class PackList { class PackList {

View File

@@ -44,11 +44,14 @@ extern Process *unknownudp;
extern Process *unknownip; extern Process *unknownip;
extern bool sortRecv; extern bool sortRecv;
extern bool sortPID;
extern int viewMode; extern int viewMode;
extern bool showcommandline; extern bool showcommandline;
extern bool showBasename; extern bool showBasename;
extern bool output_json;
extern unsigned refreshlimit; extern unsigned refreshlimit;
extern unsigned refreshcount; 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 // All descriptions are padded to 6 characters in length with spaces
const char *const desc_view_mode[VIEWMODE_COUNT] = { 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 = '/'; constexpr char FILE_SEPARATOR = '/';
@@ -90,12 +93,11 @@ public:
void show(int row, unsigned int proglen, unsigned int devlen); void show(int row, unsigned int proglen, unsigned int devlen);
void log(); void log();
void json();
double sent_value; double sent_value;
double recv_value; double recv_value;
const char *devicename; const char *devicename;
private:
const char *m_name; const char *m_name;
const char *m_cmdline; const char *m_cmdline;
pid_t m_pid; pid_t m_pid;
@@ -232,6 +234,48 @@ void Line::log() {
<< recv_value << std::endl; << recv_value << std::endl;
} }
#include <iomanip>
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<int>(*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 get_devlen(Line *lines[], int nproc, int rows) {
int devlen = MIN_COLUMN_WIDTH_DEV; int devlen = MIN_COLUMN_WIDTH_DEV;
int curlen; int curlen;
@@ -255,14 +299,18 @@ int GreatestFirst(const void *ma, const void *mb) {
Line *a = *pa; Line *a = *pa;
Line *b = *pb; Line *b = *pb;
double aValue; double aValue;
if (sortRecv) { if (sortPID) {
aValue = a->m_pid;
} else if (sortRecv) {
aValue = a->recv_value; aValue = a->recv_value;
} else { } else {
aValue = a->sent_value; aValue = a->sent_value;
} }
double bValue; double bValue;
if (sortRecv) { if (sortPID) {
bValue = (double)b->m_pid;
} else if (sortRecv) {
bValue = b->recv_value; bValue = b->recv_value;
} else { } else {
bValue = b->sent_value; 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) { void show_ncurses(Line *lines[], int nproc) {
int rows; // number of terminal rows int rows; // number of terminal rows
int cols; // number of terminal columns int cols; // number of terminal columns
@@ -436,6 +506,8 @@ void do_refresh() {
curproc->getVal()->gettotalmb(&value_recv, &value_sent); curproc->getVal()->gettotalmb(&value_recv, &value_sent);
} else if (viewMode == VIEWMODE_TOTAL_B) { } else if (viewMode == VIEWMODE_TOTAL_B) {
curproc->getVal()->gettotalb(&value_recv, &value_sent); curproc->getVal()->gettotalb(&value_recv, &value_sent);
} else if (viewMode == VIEWMODE_BPS) {
curproc->getVal()->getbps(&value_recv, &value_sent);
} else { } else {
forceExit(false, "Invalid viewMode: %d", viewMode); forceExit(false, "Invalid viewMode: %d", viewMode);
} }
@@ -453,7 +525,9 @@ void do_refresh() {
/* sort the accumulated lines */ /* sort the accumulated lines */
qsort(lines, nproc, sizeof(Line *), GreatestFirst); qsort(lines, nproc, sizeof(Line *), GreatestFirst);
if (tracemode || DEBUG) if (output_json)
show_json(lines, nproc);
else if (tracemode || DEBUG)
show_trace(lines, nproc); show_trace(lines, nproc);
else else
show_ncurses(lines, nproc); show_ncurses(lines, nproc);

View File

@@ -35,7 +35,7 @@
bool catchall = false; bool catchall = false;
/* functions to set up a handle (which is basically just a pcap handle) */ /* 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 *retval =
(struct dp_handle *)malloc(sizeof(struct dp_handle)); (struct dp_handle *)malloc(sizeof(struct dp_handle));
int i; int i;
@@ -47,37 +47,39 @@ struct dp_handle *dp_fillhandle(pcap_t *phandle) {
retval->linktype = pcap_datalink(retval->pcap_handle); retval->linktype = pcap_datalink(retval->pcap_handle);
switch (retval->linktype) { if((!quiet)){
case (DLT_EN10MB): switch (retval->linktype) {
fprintf(stdout, "Ethernet link detected\n"); case (DLT_EN10MB):
break; fprintf(stdout, "Ethernet link detected\n");
case (DLT_PPP): break;
fprintf(stdout, "PPP link detected\n"); case (DLT_PPP):
break; fprintf(stdout, "PPP link detected\n");
case (DLT_LINUX_SLL): break;
fprintf(stdout, "Linux Cooked Socket link detected\n"); case (DLT_LINUX_SLL):
break; fprintf(stdout, "Linux Cooked Socket link detected\n");
default: break;
fprintf(stdout, "No PPP or Ethernet link: %d\n", retval->linktype); default:
// TODO maybe error? or 'other' callback? fprintf(stdout, "No PPP or Ethernet link: %d\n", retval->linktype);
break; // TODO maybe error? or 'other' callback?
break;
}
} }
return retval; 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); pcap_t *temp = pcap_open_offline(fname, ebuf);
if (temp == NULL) { if (temp == NULL) {
return 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, 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 struct bpf_program fp; // compiled filter program
bpf_u_int32 maskp; // subnet mask bpf_u_int32 maskp; // subnet mask
bpf_u_int32 netp; // interface IP 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 */ /* function to get packet statistics, e.g. dropped packets */

View File

@@ -67,8 +67,8 @@ struct dp_handle {
/* functions to set up a handle (which is basically just a pcap 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, 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 dp_handle *dp_open_offline(char *fname, char *ebuf); struct dp_handle *dp_open_offline(char *fname, char *ebuf, bool quiet);
/* function to get packet statistics, e.g. dropped packets */ /* function to get packet statistics, e.g. dropped packets */

View File

@@ -39,7 +39,7 @@ int main(int argc, char **argv) {
char *errbuf = new char[DP_ERRBUF_SIZE]; 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); dp_addcb(newhandle, dp_packet_tcp, process_tcp);
int ret = dp_dispatch(newhandle, -1, NULL, 0); int ret = dp_dispatch(newhandle, -1, NULL, 0);
if (ret == -1) { if (ret == -1) {

View File

@@ -102,7 +102,7 @@ static int nethogsmonitor_init(int devc, char **devicenames, bool all,
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
dp_handle *newhandle = dp_open_live(current_dev->name, BUFSIZ, promiscuous, dp_handle *newhandle = dp_open_live(current_dev->name, BUFSIZ, promiscuous,
to_ms, filter, errbuf); to_ms, filter, errbuf, false);
if (newhandle != NULL) { if (newhandle != NULL) {
dp_addcb(newhandle, dp_packet_ip, process_ip); dp_addcb(newhandle, dp_packet_ip, process_ip);
dp_addcb(newhandle, dp_packet_ip6, process_ip6); dp_addcb(newhandle, dp_packet_ip6, process_ip6);

View File

@@ -39,7 +39,7 @@ static void help(bool iserror) {
output << " -d : delay for update refresh rate in seconds. default " output << " -d : delay for update refresh rate in seconds. default "
"is 1.\n"; "is 1.\n";
output << " -v : view mode (0 = kB/s, 1 = total kB, 2 = " 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 << " -c : number of updates. default is 0 (unlimited).\n";
output << " -t : tracemode.\n"; output << " -t : tracemode.\n";
// output << " -f : format of packets on interface, default is // 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 << " b: display the program basename instead of the fullpath\n";
output << " m: switch between total (kB, bytes, MB) and throughput (kB/s, " output << " m: switch between total (kB, bytes, MB) and throughput (kB/s, "
" MB/s, GB/s) mode\n"; " MB/s, GB/s) mode\n";
output << " j: json output\n";
output << " z: sort by PIDn";
} }
void quit_cb(int /* i */) { void quit_cb(int /* i */) {
@@ -80,7 +82,7 @@ void quit_cb(int /* i */) {
} }
void forceExit(bool success, const char *msg, ...) { void forceExit(bool success, const char *msg, ...) {
if ((!tracemode) && (!DEBUG)) { if ((!tracemode) && (!DEBUG) && (!output_json)) {
exit_ui(); exit_ui();
} }
@@ -141,7 +143,7 @@ void clean_up() {
} }
procclean(); procclean();
if ((!tracemode) && (!DEBUG)) if ((!tracemode) && (!DEBUG) && (!output_json))
exit_ui(); exit_ui();
} }
@@ -153,7 +155,7 @@ int main(int argc, char **argv) {
int garbage_collection_period = 50; int garbage_collection_period = 50;
int opt; 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) { switch (opt) {
case 'V': case 'V':
versiondisplay(); versiondisplay();
@@ -204,6 +206,12 @@ int main(int argc, char **argv) {
case 'P': case 'P':
pidsToWatch.insert((pid_t)atoi(optarg)); pidsToWatch.insert((pid_t)atoi(optarg));
break; break;
case 'j':
output_json = true;
break;
case 'z':
sortPID = true;
break;
default: default:
help(true); help(true);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -246,8 +254,9 @@ int main(int argc, char **argv) {
forceExit(false, "getifaddrs failed while establishing local IP."); forceExit(false, "getifaddrs failed while establishing local IP.");
} }
bool quiet = output_json;
dp_handle *newhandle = 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) { if (newhandle != NULL) {
dp_addcb(newhandle, dp_packet_ip, process_ip); dp_addcb(newhandle, dp_packet_ip, process_ip);
dp_addcb(newhandle, dp_packet_ip6, process_ip6); 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)); struct dpargs *userdata = (dpargs *)malloc(sizeof(struct dpargs));
if ((!tracemode) && (!DEBUG)) { if ((!tracemode) && (!DEBUG) && (!output_json)) {
init_ui(); init_ui();
} }
@@ -328,7 +337,7 @@ int main(int argc, char **argv) {
time_t const now = ::time(NULL); time_t const now = ::time(NULL);
if (last_refresh_time + refreshdelay <= now) { if (last_refresh_time + refreshdelay <= now) {
last_refresh_time = now; last_refresh_time = now;
if ((!DEBUG) && (!tracemode)) { if ((!DEBUG) && (!tracemode) && (!output_json)) {
// handle user input // handle user input
ui_tick(); ui_tick();
} }

View File

@@ -59,10 +59,12 @@ bool tracemode = false;
bool bughuntmode = false; bool bughuntmode = false;
// sort on sent or received? // sort on sent or received?
bool sortRecv = true; bool sortRecv = true;
bool sortPID = false;
bool showcommandline = false; bool showcommandline = false;
bool showBasename = false; bool showBasename = false;
// viewMode: kb/s or total // viewMode: kb/s or total
int viewMode = VIEWMODE_KBPS; int viewMode = VIEWMODE_KBPS;
bool output_json = false;
const char version[] = " version " VERSION; const char version[] = " version " VERSION;
timeval curtime; timeval curtime;

View File

@@ -70,6 +70,7 @@ enum {
VIEWMODE_TOTAL_MB, VIEWMODE_TOTAL_MB,
VIEWMODE_MBPS, VIEWMODE_MBPS,
VIEWMODE_GBPS, VIEWMODE_GBPS,
VIEWMODE_BPS,
VIEWMODE_COUNT VIEWMODE_COUNT
}; };

View File

@@ -83,10 +83,12 @@ float togbps(u_int64_t bytes) { return (((double)bytes) / PERIOD) / GB; }
void process_init() { void process_init() {
unknowntcp = new Process(0, "", "unknown TCP"); unknowntcp = new Process(0, "", "unknown TCP");
unknowntcp->keep = true;
processes = new ProcList(unknowntcp, NULL); processes = new ProcList(unknowntcp, NULL);
if (catchall) { if (catchall) {
unknownudp = new Process(0, "", "unknown UDP"); unknownudp = new Process(0, "", "unknown UDP");
unknownudp->keep = true;
processes = new ProcList(unknownudp, processes); processes = new ProcList(unknownudp, processes);
// unknownip = new Process (0, "", "unknown IP"); // unknownip = new Process (0, "", "unknown IP");
// processes = new ProcList (unknownip, processes); // 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 */ /** Get the kb/s values for this process */
void Process::getkbps(float *recvd, float *sent) { void Process::getkbps(float *recvd, float *sent) {
u_int64_t sum_sent = 0, sum_recv = 0; 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;
}
}
}

View File

@@ -79,6 +79,7 @@ public:
rcvd_by_closed_bytes = 0; rcvd_by_closed_bytes = 0;
sent_last_reported = 0; sent_last_reported = 0;
rcvd_last_reported = 0; rcvd_last_reported = 0;
keep = false;
} }
void check() { assert(pid >= 0); } void check() { assert(pid >= 0); }
@@ -91,6 +92,7 @@ public:
int getLastPacket(); int getLastPacket();
void gettotal(u_int64_t *recvd, u_int64_t *sent); void gettotal(u_int64_t *recvd, u_int64_t *sent);
void getbps(float *recvd, float *sent);
void getkbps(float *recvd, float *sent); void getkbps(float *recvd, float *sent);
void getmbps(float *recvd, float *sent); void getmbps(float *recvd, float *sent);
void getgbps(float *recvd, float *sent); void getgbps(float *recvd, float *sent);
@@ -116,6 +118,8 @@ public:
unsigned long getInode() { return inode; } unsigned long getInode() { return inode; }
bool keep;
private: private:
const unsigned long inode; const unsigned long inode;
uid_t uid; uid_t uid;