Compare commits
10 Commits
02c1811f9e
...
e13522ea35
| Author | SHA1 | Date | |
|---|---|---|---|
| e13522ea35 | |||
| 5b92953241 | |||
| 23289acabd | |||
| 59cfb62336 | |||
| 5bb454cb03 | |||
| 468b055918 | |||
| 1d8d69bf06 | |||
| 7ea25e917f | |||
| 8b6c7c7bc8 | |||
|
|
08daf76d81 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -10,3 +10,7 @@ libnethogs.a
|
|||||||
results/
|
results/
|
||||||
build/
|
build/
|
||||||
nethogs.egg-info
|
nethogs.egg-info
|
||||||
|
|
||||||
|
# IDE configurations
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
12
README.md
12
README.md
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -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
28
monitor-service/README.md
Normal 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
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
1000
monitor-service/nethogs-monitor-dashboard-template.html
Normal file
1000
monitor-service/nethogs-monitor-dashboard-template.html
Normal file
File diff suppressed because it is too large
Load Diff
22
monitor-service/nethogs-monitor-install.sh
Normal file
22
monitor-service/nethogs-monitor-install.sh
Normal 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
|
||||||
136
monitor-service/nethogs-monitor-report.sh
Normal file
136
monitor-service/nethogs-monitor-report.sh
Normal 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 "$@"
|
||||||
31
monitor-service/nethogs-monitor.service
Normal file
31
monitor-service/nethogs-monitor.service
Normal 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
|
||||||
33
monitor-service/nethogs-monitor.sh
Normal file
33
monitor-service/nethogs-monitor.sh
Normal 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
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
86
src/cui.cpp
86
src/cui.cpp
@@ -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);
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
23
src/main.cpp
23
src/main.cpp
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user