Merge pull request #108 from anomen-s/issue-23-r
Add -l option to show full command line (fixes #23)
This commit is contained in:
@@ -15,6 +15,7 @@ nethogs \- Net top tool grouping bandwidth per process
|
|||||||
.RB [ "\-p" ]
|
.RB [ "\-p" ]
|
||||||
.RB [ "\-a" ]
|
.RB [ "\-a" ]
|
||||||
.RB [ "\-s" ]
|
.RB [ "\-s" ]
|
||||||
|
.RB [ "\-l" ]
|
||||||
.RI [device(s)]
|
.RI [device(s)]
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
NetHogs is a small 'net top' tool. Instead of breaking the traffic down per protocol or per subnet, like most such tools do, it groups bandwidth by process - and does not rely on a special kernel module to be loaded. So if there's suddenly a lot of network traffic, you can fire up NetHogs and immediately see which PID is causing this, and if it's some kind of spinning process, kill it.
|
NetHogs is a small 'net top' tool. Instead of breaking the traffic down per protocol or per subnet, like most such tools do, it groups bandwidth by process - and does not rely on a special kernel module to be loaded. So if there's suddenly a lot of network traffic, you can fire up NetHogs and immediately see which PID is causing this, and if it's some kind of spinning process, kill it.
|
||||||
@@ -47,6 +48,9 @@ limit number of refreshes
|
|||||||
.TP
|
.TP
|
||||||
\fB-s\fP
|
\fB-s\fP
|
||||||
sort by traffic sent
|
sort by traffic sent
|
||||||
|
.TP
|
||||||
|
\fB-l\fP
|
||||||
|
display command line
|
||||||
.PP
|
.PP
|
||||||
.I device(s)
|
.I device(s)
|
||||||
to monitor. By default eth0 is being used
|
to monitor. By default eth0 is being used
|
||||||
@@ -56,8 +60,11 @@ to monitor. By default eth0 is being used
|
|||||||
m
|
m
|
||||||
cycle between display modes (KB/s, KB, B, MB)
|
cycle between display modes (KB/s, KB, B, MB)
|
||||||
.TP
|
.TP
|
||||||
|
l
|
||||||
|
display command line
|
||||||
|
.TP
|
||||||
r
|
r
|
||||||
sort by 'received'
|
sort by 'received'
|
||||||
.TP
|
.TP
|
||||||
s
|
s
|
||||||
sort by 'sent'
|
sort by 'sent'
|
||||||
|
|||||||
79
src/cui.cpp
79
src/cui.cpp
@@ -45,6 +45,7 @@ extern Process *unknownip;
|
|||||||
extern bool sortRecv;
|
extern bool sortRecv;
|
||||||
|
|
||||||
extern int viewMode;
|
extern int viewMode;
|
||||||
|
extern bool showcommandline;
|
||||||
|
|
||||||
extern unsigned refreshlimit;
|
extern unsigned refreshlimit;
|
||||||
extern unsigned refreshcount;
|
extern unsigned refreshcount;
|
||||||
@@ -64,11 +65,12 @@ const char *COLUMN_FORMAT_RECEIVED = "%11.3f";
|
|||||||
|
|
||||||
class Line {
|
class Line {
|
||||||
public:
|
public:
|
||||||
Line(const char *name, double n_recv_value, double n_sent_value, pid_t pid,
|
Line(const char *name, const char *cmdline, double n_recv_value,
|
||||||
uid_t uid, const char *n_devicename) {
|
double n_sent_value, pid_t pid, uid_t uid, const char *n_devicename) {
|
||||||
assert(pid >= 0);
|
assert(pid >= 0);
|
||||||
assert(pid <= PID_MAX);
|
assert(pid <= PID_MAX);
|
||||||
m_name = name;
|
m_name = name;
|
||||||
|
m_cmdline = cmdline;
|
||||||
sent_value = n_sent_value;
|
sent_value = n_sent_value;
|
||||||
recv_value = n_recv_value;
|
recv_value = n_recv_value;
|
||||||
devicename = n_devicename;
|
devicename = n_devicename;
|
||||||
@@ -85,6 +87,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const char *m_name;
|
const char *m_name;
|
||||||
|
const char *m_cmdline;
|
||||||
const char *devicename;
|
const char *devicename;
|
||||||
pid_t m_pid;
|
pid_t m_pid;
|
||||||
uid_t m_uid;
|
uid_t m_uid;
|
||||||
@@ -118,23 +121,6 @@ std::string uid2username(uid_t uid) {
|
|||||||
return std::string(pwd->pw_name);
|
return std::string(pwd->pw_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the provided text at the specified location, truncating if the length
|
|
||||||
* of the text exceeds a maximum. If the
|
|
||||||
* text must be truncated, the string ".." will be rendered, followed by max_len
|
|
||||||
* - 2 characters of the provided text.
|
|
||||||
*/
|
|
||||||
static void mvaddstr_truncate_leading(int row, int col, const char *str,
|
|
||||||
std::size_t str_len,
|
|
||||||
std::size_t max_len) {
|
|
||||||
if (str_len < max_len) {
|
|
||||||
mvaddstr(row, col, str);
|
|
||||||
} else {
|
|
||||||
mvaddstr(row, col, "..");
|
|
||||||
addnstr(str + 2, max_len - 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the provided text at the specified location, truncating if the length
|
* Render the provided text at the specified location, truncating if the length
|
||||||
* of the text exceeds a maximum. If the
|
* of the text exceeds a maximum. If the
|
||||||
@@ -152,6 +138,43 @@ static void mvaddstr_truncate_trailing(int row, int col, const char *str,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the provided progname and cmdline at the specified location,
|
||||||
|
* truncating if the length of the values exceeds a maximum.
|
||||||
|
* If the text must be truncated, the text will be rendered up to max_len - 2
|
||||||
|
* characters and then ".." will be rendered.
|
||||||
|
* cmdline is truncated first and then progname.
|
||||||
|
*/
|
||||||
|
static void mvaddstr_truncate_cmdline(int row, int col, const char *progname,
|
||||||
|
const char *cmdline,
|
||||||
|
std::size_t max_len) {
|
||||||
|
std::size_t proglen = strlen(progname);
|
||||||
|
std::size_t max_cmdlen;
|
||||||
|
|
||||||
|
if (proglen > max_len) {
|
||||||
|
mvaddnstr(row, col, progname, max_len - 2);
|
||||||
|
addstr("..");
|
||||||
|
max_cmdlen = 0;
|
||||||
|
} else {
|
||||||
|
mvaddstr(row, col, progname);
|
||||||
|
max_cmdlen = max_len - proglen - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showcommandline && cmdline) {
|
||||||
|
|
||||||
|
std::size_t cmdlinelen = strlen(cmdline);
|
||||||
|
|
||||||
|
if ((cmdlinelen + 1) > max_cmdlen) {
|
||||||
|
if (max_cmdlen >= 3) {
|
||||||
|
mvaddnstr(row, col + proglen + 1, cmdline, max_cmdlen - 3);
|
||||||
|
addstr("..");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mvaddstr(row, col + proglen + 1, cmdline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Line::show(int row, unsigned int proglen) {
|
void Line::show(int row, unsigned int proglen) {
|
||||||
assert(m_pid >= 0);
|
assert(m_pid >= 0);
|
||||||
assert(m_pid <= PID_MAX);
|
assert(m_pid <= PID_MAX);
|
||||||
@@ -175,7 +198,7 @@ void Line::show(int row, unsigned int proglen) {
|
|||||||
mvaddstr_truncate_trailing(row, column_offset_user, username.c_str(),
|
mvaddstr_truncate_trailing(row, column_offset_user, username.c_str(),
|
||||||
username.size(), COLUMN_WIDTH_USER);
|
username.size(), COLUMN_WIDTH_USER);
|
||||||
|
|
||||||
mvaddstr_truncate_leading(row, column_offset_program, m_name, strlen(m_name),
|
mvaddstr_truncate_cmdline(row, column_offset_program, m_name, m_cmdline,
|
||||||
proglen);
|
proglen);
|
||||||
|
|
||||||
mvaddstr(row, column_offset_dev, devicename);
|
mvaddstr(row, column_offset_dev, devicename);
|
||||||
@@ -195,8 +218,10 @@ void Line::show(int row, unsigned int proglen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Line::log() {
|
void Line::log() {
|
||||||
std::cout << m_name << '/' << m_pid << '/' << m_uid << "\t" << sent_value
|
std::cout << m_name;
|
||||||
<< "\t" << recv_value << std::endl;
|
if (showcommandline && m_cmdline)
|
||||||
|
std::cout << ' ' << m_cmdline;
|
||||||
|
std::cout << '/' << m_pid << '/' << m_uid << "\t" << sent_value << "\t" << recv_value << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GreatestFirst(const void *ma, const void *mb) {
|
int GreatestFirst(const void *ma, const void *mb) {
|
||||||
@@ -258,6 +283,10 @@ void ui_tick() {
|
|||||||
/* sort on 'received' */
|
/* sort on 'received' */
|
||||||
sortRecv = true;
|
sortRecv = true;
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
/* show cmdline' */
|
||||||
|
showcommandline = !showcommandline;
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
/* switch mode: total vs kb/s */
|
/* switch mode: total vs kb/s */
|
||||||
viewMode = (viewMode + 1) % VIEWMODE_COUNT;
|
viewMode = (viewMode + 1) % VIEWMODE_COUNT;
|
||||||
@@ -386,9 +415,9 @@ void do_refresh() {
|
|||||||
assert(curproc->getVal()->pid >= 0);
|
assert(curproc->getVal()->pid >= 0);
|
||||||
assert(n < nproc);
|
assert(n < nproc);
|
||||||
|
|
||||||
lines[n] =
|
lines[n] = new Line(curproc->getVal()->name, curproc->getVal()->cmdline,
|
||||||
new Line(curproc->getVal()->name, value_recv, value_sent,
|
value_recv, value_sent, curproc->getVal()->pid, uid,
|
||||||
curproc->getVal()->pid, uid, curproc->getVal()->devicename);
|
curproc->getVal()->devicename);
|
||||||
curproc = curproc->next;
|
curproc = curproc->next;
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,12 +116,32 @@ static std::string read_file(const char *filepath) {
|
|||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getprogname(pid_t pid) {
|
std::string getcmdline(pid_t pid) {
|
||||||
const int maxfilenamelen = 14 + MAX_PID_LENGTH + 1;
|
const int maxfilenamelen = 14 + MAX_PID_LENGTH + 1;
|
||||||
char filename[maxfilenamelen];
|
char filename[maxfilenamelen];
|
||||||
|
|
||||||
std::snprintf(filename, maxfilenamelen, "/proc/%d/cmdline", pid);
|
std::snprintf(filename, maxfilenamelen, "/proc/%d/cmdline", pid);
|
||||||
return read_file(filename);
|
|
||||||
|
bool replace_null = false;
|
||||||
|
std::string cmdline = read_file(filename);
|
||||||
|
|
||||||
|
// join parameters, keep prgname separate, don't overwrite trailing null
|
||||||
|
for (size_t idx = 0; idx < (cmdline.length() - 1); idx++) {
|
||||||
|
if (cmdline[idx] == 0x00) {
|
||||||
|
if (replace_null) {
|
||||||
|
cmdline[idx] = ' ';
|
||||||
|
}
|
||||||
|
replace_null = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmdline.length() == 0 || (cmdline[cmdline.length() - 1] != 0x00)) {
|
||||||
|
// invalid content of cmdline file. Add null char to allow further
|
||||||
|
// processing.
|
||||||
|
cmdline.append("\0");
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdline;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setnode(unsigned long inode, pid_t pid) {
|
void setnode(unsigned long inode, pid_t pid) {
|
||||||
@@ -131,7 +151,7 @@ void setnode(unsigned long inode, pid_t pid) {
|
|||||||
prg_node *newnode = new prg_node;
|
prg_node *newnode = new prg_node;
|
||||||
newnode->inode = inode;
|
newnode->inode = inode;
|
||||||
newnode->pid = pid;
|
newnode->pid = pid;
|
||||||
newnode->name = getprogname(pid);
|
newnode->cmdline = getcmdline(pid);
|
||||||
|
|
||||||
inodeproc[inode] = newnode;
|
inodeproc[inode] = newnode;
|
||||||
delete current_value;
|
delete current_value;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
struct prg_node {
|
struct prg_node {
|
||||||
long inode;
|
long inode;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
std::string name;
|
std::string cmdline;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct prg_node *findPID(unsigned long inode);
|
struct prg_node *findPID(unsigned long inode);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ static void help(bool iserror) {
|
|||||||
// output << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [-f (eth|ppp))]
|
// output << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [-f (eth|ppp))]
|
||||||
// [device [device [device ...]]]\n";
|
// [device [device [device ...]]]\n";
|
||||||
output << "usage: nethogs [-V] [-h] [-b] [-d seconds] [-v mode] [-c count] "
|
output << "usage: nethogs [-V] [-h] [-b] [-d seconds] [-v mode] [-c count] "
|
||||||
"[-t] [-p] [-s] [-a] [device [device [device ...]]]\n";
|
"[-t] [-p] [-s] [-a] [-l] [device [device [device ...]]]\n";
|
||||||
output << " -V : prints version.\n";
|
output << " -V : prints version.\n";
|
||||||
output << " -h : prints this help.\n";
|
output << " -h : prints this help.\n";
|
||||||
output << " -b : bughunt mode - implies tracemode.\n";
|
output << " -b : bughunt mode - implies tracemode.\n";
|
||||||
@@ -41,6 +41,7 @@ static void help(bool iserror) {
|
|||||||
// eth.\n";
|
// eth.\n";
|
||||||
output << " -p : sniff in promiscious mode (not recommended).\n";
|
output << " -p : sniff in promiscious mode (not recommended).\n";
|
||||||
output << " -s : sort output by sent column.\n";
|
output << " -s : sort output by sent column.\n";
|
||||||
|
output << " -l : display command line.\n";
|
||||||
output << " -a : monitor all devices, even loopback/stopped ones.\n";
|
output << " -a : monitor all devices, even loopback/stopped ones.\n";
|
||||||
output << " device : device(s) to monitor. default is all "
|
output << " device : device(s) to monitor. default is all "
|
||||||
"interfaces up and running excluding loopback\n";
|
"interfaces up and running excluding loopback\n";
|
||||||
@@ -49,6 +50,7 @@ static void help(bool iserror) {
|
|||||||
output << " q: quit\n";
|
output << " q: quit\n";
|
||||||
output << " s: sort by SENT traffic\n";
|
output << " s: sort by SENT traffic\n";
|
||||||
output << " r: sort by RECEIVE traffic\n";
|
output << " r: sort by RECEIVE traffic\n";
|
||||||
|
output << " l: display command line\n";
|
||||||
output << " m: switch between total (KB, B, MB) and KB/s mode\n";
|
output << " m: switch between total (KB, B, MB) and KB/s mode\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +135,7 @@ int main(int argc, char **argv) {
|
|||||||
bool all = false;
|
bool all = false;
|
||||||
|
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "Vhbtpsd:v:c:a")) != -1) {
|
while ((opt = getopt(argc, argv, "Vhbtpsd:v:c:la")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'V':
|
case 'V':
|
||||||
versiondisplay();
|
versiondisplay();
|
||||||
@@ -163,6 +165,9 @@ int main(int argc, char **argv) {
|
|||||||
case 'c':
|
case 'c':
|
||||||
refreshlimit = atoi(optarg);
|
refreshlimit = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
showcommandline = true;
|
||||||
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
all = true;
|
all = true;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ 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 showcommandline = false;
|
||||||
// viewMode: kb/s or total
|
// viewMode: kb/s or total
|
||||||
int viewMode = VIEWMODE_KBPS;
|
int viewMode = VIEWMODE_KBPS;
|
||||||
const char version[] = " version " VERSION;
|
const char version[] = " version " VERSION;
|
||||||
|
|||||||
@@ -225,7 +225,11 @@ Process *getProcess(unsigned long inode, const char *devicename) {
|
|||||||
if (proc != NULL)
|
if (proc != NULL)
|
||||||
return proc;
|
return proc;
|
||||||
|
|
||||||
Process *newproc = new Process(inode, devicename, node->name.c_str());
|
// extract program name and command line from data read from cmdline file
|
||||||
|
const char *prgname = node->cmdline.c_str();
|
||||||
|
const char *cmdline = prgname + strlen(prgname) + 1;
|
||||||
|
|
||||||
|
Process *newproc = new Process(inode, devicename, prgname, cmdline);
|
||||||
newproc->pid = node->pid;
|
newproc->pid = node->pid;
|
||||||
|
|
||||||
char procdir[100];
|
char procdir[100];
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public:
|
|||||||
/* the process makes a copy of the name. the device name needs to be stable.
|
/* the process makes a copy of the name. the device name needs to be stable.
|
||||||
*/
|
*/
|
||||||
Process(const unsigned long m_inode, const char *m_devicename,
|
Process(const unsigned long m_inode, const char *m_devicename,
|
||||||
const char *m_name = NULL)
|
const char *m_name = NULL, const char *m_cmdline = NULL)
|
||||||
: inode(m_inode) {
|
: inode(m_inode) {
|
||||||
// std::cout << "ARN: Process created with dev " << m_devicename <<
|
// std::cout << "ARN: Process created with dev " << m_devicename <<
|
||||||
// std::endl;
|
// std::endl;
|
||||||
@@ -69,6 +69,11 @@ public:
|
|||||||
else
|
else
|
||||||
name = strdup(m_name);
|
name = strdup(m_name);
|
||||||
|
|
||||||
|
if (m_cmdline == NULL)
|
||||||
|
cmdline = NULL;
|
||||||
|
else
|
||||||
|
cmdline = strdup(m_cmdline);
|
||||||
|
|
||||||
devicename = m_devicename;
|
devicename = m_devicename;
|
||||||
connections = NULL;
|
connections = NULL;
|
||||||
pid = 0;
|
pid = 0;
|
||||||
@@ -78,6 +83,7 @@ public:
|
|||||||
|
|
||||||
~Process() {
|
~Process() {
|
||||||
free(name);
|
free(name);
|
||||||
|
free(cmdline);
|
||||||
if (DEBUG)
|
if (DEBUG)
|
||||||
std::cout << "PROC: Process deleted at " << this << std::endl;
|
std::cout << "PROC: Process deleted at " << this << std::endl;
|
||||||
}
|
}
|
||||||
@@ -90,6 +96,7 @@ public:
|
|||||||
void gettotalb(float *recvd, float *sent);
|
void gettotalb(float *recvd, float *sent);
|
||||||
|
|
||||||
char *name;
|
char *name;
|
||||||
|
char *cmdline;
|
||||||
const char *devicename;
|
const char *devicename;
|
||||||
int pid;
|
int pid;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user