/* * cui.cpp * * Copyright (c) 2004-2006,2008,2010,2011 Arnout Engelen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /* NetHogs console UI */ #include #include #include #include #include #include #include #include "nethogs.h" #include "process.h" std::string * caption; //extern char [] version; const char version[] = " version " VERSION "." SUBVERSION "." MINORVERSION; extern ProcList * processes; extern timeval curtime; extern Process * unknowntcp; extern Process * unknownudp; extern Process * unknownip; // sort on sent or received? bool sortRecv = true; // viewMode: kb/s or total int VIEWMODE_KBPS = 0; int VIEWMODE_TOTAL_KB = 1; int VIEWMODE_TOTAL_B = 2; int VIEWMODE_TOTAL_MB = 3; int viewMode = VIEWMODE_KBPS; int nViewModes = 4; 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) { assert (pid >= 0); m_name = name; sent_value = n_sent_value; recv_value = n_recv_value; devicename = n_devicename; m_pid = pid; m_uid = uid; assert (m_pid >= 0); } void show (int row, unsigned int proglen); double sent_value; double recv_value; private: const char * m_name; const char * devicename; pid_t m_pid; uid_t m_uid; }; char * uid2username (uid_t uid) { struct passwd * pwd = NULL; /* getpwuid() allocates space for this itself, * which we shouldn't free */ pwd = getpwuid(uid); if (pwd == NULL) { assert(false); return strdup ("unlisted"); } else { return strdup(pwd->pw_name); } } void Line::show (int row, unsigned int proglen) { assert (m_pid >= 0); assert (m_pid <= 100000); if (DEBUG || tracemode) { std::cout << m_name << '/' << m_pid << '/' << m_uid << "\t" << sent_value << "\t" << recv_value << std::endl; return; } if (m_pid == 0) mvprintw (3+row, 0, "?"); else mvprintw (3+row, 0, "%d", m_pid); char * username = uid2username(m_uid); mvprintw (3+row, 6, "%s", username); free (username); if (strlen (m_name) > proglen) { // truncate oversized names char * tmp = strdup(m_name); char * start = tmp + strlen (m_name) - proglen; start[0] = '.'; start[1] = '.'; mvprintw (3+row, 6 + 9, "%s", start); free (tmp); } else { mvprintw (3+row, 6 + 9, "%s", m_name); } mvprintw (3+row, 6 + 9 + proglen + 2, "%s", devicename); mvprintw (3+row, 6 + 9 + proglen + 2 + 6, "%10.3f", sent_value); mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3, "%10.3f", recv_value); if (viewMode == VIEWMODE_KBPS) { mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "KB/sec"); } else if (viewMode == VIEWMODE_TOTAL_MB) { mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "MB "); } else if (viewMode == VIEWMODE_TOTAL_KB) { mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "KB "); } else if (viewMode == VIEWMODE_TOTAL_B) { mvprintw (3+row, 6 + 9 + proglen + 2 + 6 + 9 + 3 + 11, "B "); } } int GreatestFirst (const void * ma, const void * mb) { Line ** pa = (Line **)ma; Line ** pb = (Line **)mb; Line * a = *pa; Line * b = *pb; double aValue; if (sortRecv) { aValue = a->recv_value; } else { aValue = a->sent_value; } double bValue; if (sortRecv) { bValue = b->recv_value; } else { bValue = b->sent_value; } if (aValue > bValue) { return -1; } if (aValue == bValue) { return 0; } return 1; } void init_ui () { WINDOW * screen = initscr(); raw(); noecho(); cbreak(); nodelay(screen, TRUE); caption = new std::string ("NetHogs"); caption->append(version); //caption->append(", running at "); } void exit_ui () { clear(); endwin(); delete caption; } void ui_tick () { switch (getch()) { case 'q': /* quit */ quit_cb(0); break; case 's': /* sort on 'sent' */ sortRecv = false; break; case 'r': /* sort on 'received' */ sortRecv = true; break; case 'm': /* switch mode: total vs kb/s */ viewMode = (viewMode + 1) % nViewModes; break; } } float tomb (u_int32_t bytes) { return ((double)bytes) / 1024 / 1024; } float tokb (u_int32_t bytes) { return ((double)bytes) / 1024; } float tokbps (u_int32_t bytes) { return (((double)bytes) / PERIOD) / 1024; } /** Get the kb/s values for this process */ void getkbps (Process * curproc, float * recvd, float * sent) { u_int32_t sum_sent = 0, sum_recv = 0; /* walk though all this process's connections, and sum * them up */ ConnList * curconn = curproc->connections; ConnList * previous = NULL; while (curconn != NULL) { if (curconn->getVal()->getLastPacket() <= curtime.tv_sec - CONNTIMEOUT) { /* stalled connection, remove. */ ConnList * todelete = curconn; Connection * conn_todelete = curconn->getVal(); curconn = curconn->getNext(); if (todelete == curproc->connections) curproc->connections = curconn; if (previous != NULL) previous->setNext(curconn); delete (todelete); delete (conn_todelete); } else { u_int32_t sent = 0, recv = 0; curconn->getVal()->sumanddel(curtime, &recv, &sent); sum_sent += sent; sum_recv += recv; previous = curconn; curconn = curconn->getNext(); } } *recvd = tokbps(sum_recv); *sent = tokbps(sum_sent); } /** get total values for this process */ void gettotal(Process * curproc, u_int32_t * recvd, u_int32_t * sent) { u_int32_t sum_sent = 0, sum_recv = 0; ConnList * curconn = curproc->connections; while (curconn != NULL) { Connection * conn = curconn->getVal(); sum_sent += conn->sumSent; sum_recv += conn->sumRecv; curconn = curconn->getNext(); } //std::cout << "Sum sent: " << sum_sent << std::endl; //std::cout << "Sum recv: " << sum_recv << std::endl; *recvd = sum_recv; *sent = sum_sent; } void gettotalmb(Process * curproc, float * recvd, float * sent) { u_int32_t sum_sent = 0, sum_recv = 0; gettotal(curproc, &sum_recv, &sum_sent); *recvd = tomb(sum_recv); *sent = tomb(sum_sent); } /** get total values for this process */ void gettotalkb(Process * curproc, float * recvd, float * sent) { u_int32_t sum_sent = 0, sum_recv = 0; gettotal(curproc, &sum_recv, &sum_sent); *recvd = tokb(sum_recv); *sent = tokb(sum_sent); } void gettotalb(Process * curproc, float * recvd, float * sent) { u_int32_t sum_sent = 0, sum_recv = 0; gettotal(curproc, &sum_recv, &sum_sent); //std::cout << "Total sent: " << sum_sent << std::endl; *sent = sum_sent; *recvd = sum_recv; } // Display all processes and relevant network traffic using show function void do_refresh() { int row; // number of terminal rows int col; // number of terminal columns unsigned int proglen; // max length of the "PROGRAM" column getmaxyx(stdscr, row, col); /* find the boundaries of the screeen */ if (col < 60) { clear(); mvprintw(0,0, "The terminal is too narrow! Please make it wider.\nI'll wait..."); return; } if (col > PROGNAME_WIDTH) col = PROGNAME_WIDTH; proglen = col - 53; refreshconninode(); if (DEBUG || tracemode) { std::cout << "\nRefreshing:\n"; } else { clear(); mvprintw (0, 0, "%s", caption->c_str()); attron(A_REVERSE); mvprintw (2, 0, " PID USER %-*.*s DEV SENT RECEIVED ", proglen, proglen, "PROGRAM"); attroff(A_REVERSE); } ProcList * curproc = processes; ProcList * previousproc = NULL; int nproc = processes->size(); /* initialise to null pointers */ Line * lines [nproc]; int n = 0, i = 0; double sent_global = 0; double recv_global = 0; #ifndef NDEBUG // initialise to null pointers for (int i = 0; i < nproc; i++) lines[i] = NULL; #endif while (curproc != NULL) { // walk though its connections, summing up their data, and // throwing away connections that haven't received a package // in the last PROCESSTIMEOUT seconds. assert (curproc != NULL); assert (curproc->getVal() != NULL); assert (nproc == processes->size()); /* remove timed-out processes (unless it's one of the the unknown process) */ if ((curproc->getVal()->getLastPacket() + PROCESSTIMEOUT <= curtime.tv_sec) && (curproc->getVal() != unknowntcp) && (curproc->getVal() != unknownudp) && (curproc->getVal() != unknownip)) { if (DEBUG) std::cout << "PROC: Deleting process\n"; ProcList * todelete = curproc; Process * p_todelete = curproc->getVal(); if (previousproc) { previousproc->next = curproc->next; curproc = curproc->next; } else { processes = curproc->getNext(); curproc = processes; } delete todelete; delete p_todelete; nproc--; //continue; } else { // add a non-timed-out process to the list of stuff to show float value_sent = 0, value_recv = 0; if (viewMode == VIEWMODE_KBPS) { //std::cout << "kbps viemode" << std::endl; getkbps (curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_KB) { //std::cout << "total viemode" << std::endl; gettotalkb(curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_MB) { //std::cout << "total viemode" << std::endl; gettotalmb(curproc->getVal(), &value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_B) { //std::cout << "total viemode" << std::endl; gettotalb(curproc->getVal(), &value_recv, &value_sent); } else { forceExit("Invalid viewmode"); } uid_t uid = curproc->getVal()->getUid(); #ifndef NDEBUG struct passwd * pwuid = getpwuid(uid); assert (pwuid != NULL); // value returned by pwuid should not be freed, according to // Petr Uzel. //free (pwuid); #endif 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); previousproc = curproc; curproc = curproc->next; n++; #ifndef NDEBUG assert (nproc == processes->size()); if (curproc == NULL) assert (n-1 < nproc); else assert (n < nproc); #endif } } /* sort the accumulated lines */ qsort (lines, nproc, sizeof(Line *), GreatestFirst); /* print them */ for (i=0; ishow(i, proglen); recv_global += lines[i]->recv_value; sent_global += lines[i]->sent_value; delete lines[i]; } if (tracemode || DEBUG) { /* print the 'unknown' connections, for debugging */ ConnList * curr_unknownconn = unknowntcp->connections; while (curr_unknownconn != NULL) { std::cout << "Unknown connection: " << curr_unknownconn->getVal()->refpacket->gethashstring() << std::endl; curr_unknownconn = curr_unknownconn->getNext(); } } if ((!tracemode) && (!DEBUG)){ attron(A_REVERSE); mvprintw (3+1+i, 0, " TOTAL %-*.*s %10.3f %10.3f ", proglen, proglen, " ", sent_global, recv_global); if (viewMode == VIEWMODE_KBPS) { mvprintw (3+1+i, col - 7, "KB/sec "); } else if (viewMode == VIEWMODE_TOTAL_B) { mvprintw (3+1+i, col - 7, "B "); } else if (viewMode == VIEWMODE_TOTAL_KB) { mvprintw (3+1+i, col - 7, "KB "); } else if (viewMode == VIEWMODE_TOTAL_MB) { mvprintw (3+1+i, col - 7, "MB "); } attroff(A_REVERSE); mvprintw (4+1+i, 0, ""); refresh(); } }