Permalink
Browse files

Add -l option to show full command line (fixes #23)

  • Loading branch information...
1 parent 276d648 commit 5e2c35700b7723ded9f03d971ea9588b0c385b77 @anomen-s anomen-s committed Jun 13, 2016
Showing with 107 additions and 17 deletions.
  1. +8 −1 doc/nethogs.8
  2. +54 −8 src/cui.cpp
  3. +23 −3 src/inode2prog.cpp
  4. +1 −1 src/inode2prog.h
  5. +7 −2 src/main.cpp
  6. +1 −0 src/nethogs.cpp
  7. +5 −1 src/process.cpp
  8. +8 −1 src/process.h
View
@@ -15,6 +15,7 @@ nethogs \- Net top tool grouping bandwidth per process
.RB [ "\-p" ]
.RB [ "\-a" ]
.RB [ "\-s" ]
+.RB [ "\-l" ]
.RI [device(s)]
.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.
@@ -47,6 +48,9 @@ limit number of refreshes
.TP
\fB-s\fP
sort by traffic sent
+.TP
+\fB-l\fP
+display command line
.PP
.I device(s)
to monitor. By default eth0 is being used
@@ -56,8 +60,11 @@ to monitor. By default eth0 is being used
m
cycle between display modes (KB/s, KB, B, MB)
.TP
+l
+display command line
+.TP
r
-sort by 'received'
+sort by 'received'
.TP
s
sort by 'sent'
View
@@ -45,6 +45,7 @@ extern Process *unknownip;
extern bool sortRecv;
extern int viewMode;
+extern bool showcommandline;
extern unsigned refreshlimit;
extern unsigned refreshcount;
@@ -64,11 +65,12 @@ const char *COLUMN_FORMAT_RECEIVED = "%11.3f";
class Line {
public:
- Line(const char *name, double n_recv_value, double n_sent_value, pid_t pid,
- uid_t uid, const char *n_devicename) {
+ Line(const char *name, const char *cmdline, double n_recv_value,
+ double n_sent_value, pid_t pid, uid_t uid, const char *n_devicename) {
assert(pid >= 0);
assert(pid <= PID_MAX);
m_name = name;
+ m_cmdline = cmdline;
sent_value = n_sent_value;
recv_value = n_recv_value;
devicename = n_devicename;
@@ -85,6 +87,7 @@ class Line {
private:
const char *m_name;
+ const char *m_cmdline;
const char *devicename;
pid_t m_pid;
uid_t m_uid;
@@ -152,6 +155,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) {
assert(m_pid >= 0);
assert(m_pid <= PID_MAX);
@@ -175,7 +215,7 @@ void Line::show(int row, unsigned int proglen) {
mvaddstr_truncate_trailing(row, column_offset_user, username.c_str(),
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);
mvaddstr(row, column_offset_dev, devicename);
@@ -195,8 +235,10 @@ void Line::show(int row, unsigned int proglen) {
}
void Line::log() {
- std::cout << m_name << '/' << m_pid << '/' << m_uid << "\t" << sent_value
- << "\t" << recv_value << std::endl;
+ std::cout << m_name;
+ 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) {
@@ -258,6 +300,10 @@ void ui_tick() {
/* sort on 'received' */
sortRecv = true;
break;
+ case 'l':
+ /* show cmdline' */
+ showcommandline = !showcommandline;
+ break;
case 'm':
/* switch mode: total vs kb/s */
viewMode = (viewMode + 1) % VIEWMODE_COUNT;
@@ -386,9 +432,9 @@ void do_refresh() {
assert(curproc->getVal()->pid >= 0);
assert(n < nproc);
- lines[n] =
- new Line(curproc->getVal()->name, value_recv, value_sent,
- curproc->getVal()->pid, uid, curproc->getVal()->devicename);
+ lines[n] = new Line(curproc->getVal()->name, curproc->getVal()->cmdline,
+ value_recv, value_sent, curproc->getVal()->pid, uid,
+ curproc->getVal()->devicename);
curproc = curproc->next;
n++;
}
View
@@ -116,12 +116,32 @@ static std::string read_file(const char *filepath) {
return contents;
}
-std::string getprogname(pid_t pid) {
+std::string getcmdline(pid_t pid) {
const int maxfilenamelen = 14 + MAX_PID_LENGTH + 1;
char filename[maxfilenamelen];
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 (int 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) {
@@ -131,7 +151,7 @@ void setnode(unsigned long inode, pid_t pid) {
prg_node *newnode = new prg_node;
newnode->inode = inode;
newnode->pid = pid;
- newnode->name = getprogname(pid);
+ newnode->cmdline = getcmdline(pid);
inodeproc[inode] = newnode;
delete current_value;
View
@@ -31,7 +31,7 @@
struct prg_node {
long inode;
pid_t pid;
- std::string name;
+ std::string cmdline;
};
struct prg_node *findPID(unsigned long inode);
View
@@ -27,7 +27,7 @@ static void help(bool iserror) {
// output << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [-f (eth|ppp))]
// [device [device [device ...]]]\n";
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 << " -h : prints this help.\n";
output << " -b : bughunt mode - implies tracemode.\n";
@@ -41,6 +41,7 @@ static void help(bool iserror) {
// eth.\n";
output << " -p : sniff in promiscious mode (not recommended).\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 << " device : device(s) to monitor. default is all "
"interfaces up and running excluding loopback\n";
@@ -49,6 +50,7 @@ static void help(bool iserror) {
output << " q: quit\n";
output << " s: sort by SENT 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";
}
@@ -133,7 +135,7 @@ int main(int argc, char **argv) {
bool all = false;
int opt;
- while ((opt = getopt(argc, argv, "Vhbtpsd:v:c:a")) != -1) {
+ while ((opt = getopt(argc, argv, "Vhbtpsd:v:c:la")) != -1) {
switch (opt) {
case 'V':
versiondisplay();
@@ -163,6 +165,9 @@ int main(int argc, char **argv) {
case 'c':
refreshlimit = atoi(optarg);
break;
+ case 'l':
+ showcommandline = true;
+ break;
case 'a':
all = true;
break;
View
@@ -59,6 +59,7 @@ bool tracemode = false;
bool bughuntmode = false;
// sort on sent or received?
bool sortRecv = true;
+bool showcommandline = false;
// viewMode: kb/s or total
int viewMode = VIEWMODE_KBPS;
const char version[] = " version " VERSION;
View
@@ -225,7 +225,11 @@ Process *getProcess(unsigned long inode, const char *devicename) {
if (proc != NULL)
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;
char procdir[100];
View
@@ -57,7 +57,7 @@ class Process {
/* 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,
- const char *m_name = NULL)
+ const char *m_name = NULL, const char *m_cmdline = NULL)
: inode(m_inode) {
// std::cout << "ARN: Process created with dev " << m_devicename <<
// std::endl;
@@ -69,6 +69,11 @@ class Process {
else
name = strdup(m_name);
+ if (m_cmdline == NULL)
+ cmdline = NULL;
+ else
+ cmdline = strdup(m_cmdline);
+
devicename = m_devicename;
connections = NULL;
pid = 0;
@@ -78,6 +83,7 @@ class Process {
~Process() {
free(name);
+ free(cmdline);
if (DEBUG)
std::cout << "PROC: Process deleted at " << this << std::endl;
}
@@ -90,6 +96,7 @@ class Process {
void gettotalb(float *recvd, float *sent);
char *name;
+ char *cmdline;
const char *devicename;
int pid;

0 comments on commit 5e2c357

Please sign in to comment.