diff --git a/doc/nethogs.8 b/doc/nethogs.8 index e291684..1c33a81 100644 --- a/doc/nethogs.8 +++ b/doc/nethogs.8 @@ -75,6 +75,9 @@ 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-f\fP EXPERIMENTAL: specify string pcap filter (like tcpdump). This may be removed or changed in a future version. .TP diff --git a/src/cui.cpp b/src/cui.cpp index 137bce6..73eeae0 100644 --- a/src/cui.cpp +++ b/src/cui.cpp @@ -49,6 +49,8 @@ extern int viewMode; extern bool showcommandline; extern bool showBasename; +extern bool output_json; + extern unsigned refreshlimit; extern unsigned refreshcount; @@ -90,6 +92,7 @@ public: void show(int row, unsigned int proglen, unsigned int devlen); void log(); + void json(); double sent_value; double recv_value; @@ -232,6 +235,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; @@ -343,6 +388,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 @@ -453,7 +520,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/main.cpp b/src/main.cpp index 7501461..4c77e27 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,6 +69,7 @@ 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"; } void quit_cb(int /* i */) { @@ -80,7 +81,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 +142,7 @@ void clean_up() { } procclean(); - if ((!tracemode) && (!DEBUG)) + if ((!tracemode) && (!DEBUG) && (!output_json)) exit_ui(); } @@ -153,7 +154,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:j")) != -1) { switch (opt) { case 'V': versiondisplay(); @@ -204,6 +205,9 @@ int main(int argc, char **argv) { case 'P': pidsToWatch.insert((pid_t)atoi(optarg)); break; + case 'j': + output_json = true; + break; default: help(true); exit(EXIT_FAILURE); @@ -300,7 +304,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 +332,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..b120699 100644 --- a/src/nethogs.cpp +++ b/src/nethogs.cpp @@ -63,6 +63,7 @@ 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;