From 8d6a3c3964fc5aaa7f4901acd8b398f109feee0c Mon Sep 17 00:00:00 2001 From: Mohamed Boussaffa Date: Mon, 7 Mar 2016 05:37:43 +0800 Subject: [PATCH 1/5] use select() for main loop --- Makefile | 5 +++ main.cpp | 118 +++++++++++++++++++++++++++++++++++++++++++++++++--- nethogs.cpp | 8 ---- 3 files changed, 118 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index cced7aa..12fee9e 100644 --- a/Makefile +++ b/Makefile @@ -16,8 +16,13 @@ runtests: test # nethogs_testsum +ifeq ($(DEBUG),1) +CFLAGS?=-Wall -Wextra -g -O0 +CXXFLAGS?=-Wall -Wextra -g -O0 +else CFLAGS?=-Wall -Wextra CXXFLAGS?=-Wall -Wextra +endif OBJS=packet.o connection.o process.o refresh.o decpcap.o cui.o inode2prog.o conninode.o devices.o diff --git a/main.cpp b/main.cpp index 2a417d5..d0256b5 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,8 @@ #include "nethogs.cpp" +#include +#include + +std::pair self_pipe = std::make_pair(-1, -1); static void versiondisplay(void) { @@ -30,6 +34,35 @@ static void help(bool iserror) output << " m: switch between total (KB, B, MB) and KB/s mode\n"; } + +void quit_cb (int /* i */) +{ + if( self_pipe.second != -1 ) + { + std::cout << "writing to exit pipe\n"; + write(self_pipe.second, "x", 1); + } + else + { + exit(0); + } +} + +std::pair createSelfPipe() +{ + int pfd[2]; + if (pipe(pfd) == -1) + return std::make_pair(-1, -1); + + if (fcntl(pfd[0], F_SETFL, fcntl(pfd[0], F_GETFL) | O_NONBLOCK) == -1) + return std::make_pair(-1, -1); + + if (fcntl(pfd[1], F_SETFL, fcntl(pfd[1], F_GETFL) | O_NONBLOCK) == -1) + return std::make_pair(-1, -1); + + return std::make_pair(pfd[0], pfd[1]); +} + int main (int argc, char** argv) { process_init(); @@ -102,9 +135,25 @@ int main (int argc, char** argv) if ((!tracemode) && (!DEBUG)){ init_ui(); } + + std::pair self_pipe = createSelfPipe(); + if( self_pipe.first == -1|| self_pipe.second == -1 ) + { + perror("Error creating pipe file descriptors\n"); + return 0; + } - if (NEEDROOT && (geteuid() != 0)) - forceExit(false, "You need to be root to run NetHogs!"); + //if (NEEDROOT && (geteuid() != 0)) + // forceExit(false, "You need to be root to run NetHogs!"); + + fd_set pc_loop_fd_set; + std::vector pc_loop_fd_list; + bool pc_loop_use_select = true; + + if( pc_loop_use_select ) + { + pc_loop_fd_list.push_back(self_pipe.first); + } char errbuf[PCAP_ERRBUF_SIZE]; @@ -132,6 +181,21 @@ int main (int argc, char** argv) fprintf(stderr, "Error putting libpcap in nonblocking mode\n"); } handles = new handle (newhandle, current_dev->name, handles); + + if( pc_loop_use_select ) + { + //some devices may not support pcap_get_selectable_fd + int const fd = pcap_get_selectable_fd(newhandle->pcap_handle); + if( fd != -1 ) + { + pc_loop_fd_list.push_back(fd); + } + else + { + pc_loop_use_select = false; + pc_loop_fd_list.clear(); + } + } } else { @@ -141,6 +205,11 @@ int main (int argc, char** argv) current_dev = current_dev->next; } + if( pc_loop_use_select ) + { + pc_loop_fd_list.push_back(self_pipe.first); + } + signal (SIGALRM, &alarm_cb); signal (SIGINT, &quit_cb); alarm (refreshdelay); @@ -185,12 +254,51 @@ int main (int argc, char** argv) needrefresh = false; } - // If no packets were read at all this iteration, pause to prevent 100% - // CPU utilisation; + //if not packets, do a select() until next packet if (!packets_read) { - usleep(100); + if( pc_loop_use_select ) + { + FD_ZERO(&pc_loop_fd_set); + int nfds = 0; + for(std::vector::const_iterator it=pc_loop_fd_list.begin(); + it != pc_loop_fd_list.end(); ++it) + { + int const fd = *it; + nfds = std::max(nfds, *it + 1); + FD_SET(fd, &pc_loop_fd_set); + } + timeval timeout = {refreshdelay, 0}; + if( select(nfds, &pc_loop_fd_set, 0, 0, &timeout) == -1 ) + { + perror("error in select()\n"); + break; + } + if( FD_ISSET(self_pipe.first, &pc_loop_fd_set) ) + { + std::cout << "exited by select\n"; + //exit the loop + break; + } + } + else + { + // If select() not possible, pause to prevent 100% + // Pause 10 milliseconds + usleep(1000); + } } } + + //close file descriptors + for(std::vector::const_iterator it=pc_loop_fd_list.begin(); + it != pc_loop_fd_list.end(); ++it) + { + close(*it); + } + + procclean(); + if ((!tracemode) && (!DEBUG)) + exit_ui(); } diff --git a/nethogs.cpp b/nethogs.cpp index 3f20df9..0860742 100644 --- a/nethogs.cpp +++ b/nethogs.cpp @@ -227,14 +227,6 @@ int process_ip6 (u_char * userdata, const dp_header * /* header */, const u_char return false; } -void quit_cb (int /* i */) -{ - procclean(); - if ((!tracemode) && (!DEBUG)) - exit_ui(); - exit(0); -} - void forceExit(bool success, const char *msg, ...) { if ((!tracemode)&&(!DEBUG)){ From cb2a699d5784b5e4e604076b024b40f76af38089 Mon Sep 17 00:00:00 2001 From: Mohamed Boussaffa Date: Mon, 7 Mar 2016 05:42:57 +0800 Subject: [PATCH 2/5] restore root test --- main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/main.cpp b/main.cpp index d0256b5..70d6bde 100644 --- a/main.cpp +++ b/main.cpp @@ -39,7 +39,6 @@ void quit_cb (int /* i */) { if( self_pipe.second != -1 ) { - std::cout << "writing to exit pipe\n"; write(self_pipe.second, "x", 1); } else @@ -143,8 +142,8 @@ int main (int argc, char** argv) return 0; } - //if (NEEDROOT && (geteuid() != 0)) - // forceExit(false, "You need to be root to run NetHogs!"); + if (NEEDROOT && (geteuid() != 0)) + forceExit(false, "You need to be root to run NetHogs!"); fd_set pc_loop_fd_set; std::vector pc_loop_fd_list; From 9015e6c27fe3d5aa214d8af3e07ccaaa177b4de1 Mon Sep 17 00:00:00 2001 From: Mohamed Boussaffa Date: Mon, 7 Mar 2016 06:13:04 +0800 Subject: [PATCH 3/5] No more need for the refresh signal because select() timeout replaces it --- Makefile | 4 +--- main.cpp | 31 +++++++++++++++---------------- nethogs.cpp | 2 -- refresh.cpp | 39 --------------------------------------- refresh.h | 23 ----------------------- 5 files changed, 16 insertions(+), 83 deletions(-) delete mode 100644 refresh.cpp delete mode 100644 refresh.h diff --git a/Makefile b/Makefile index 12fee9e..35638fe 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CFLAGS?=-Wall -Wextra CXXFLAGS?=-Wall -Wextra endif -OBJS=packet.o connection.o process.o refresh.o decpcap.o cui.o inode2prog.o conninode.o devices.o +OBJS=packet.o connection.o process.o decpcap.o cui.o inode2prog.o conninode.o devices.o NCURSES_LIBS?=-lncurses @@ -64,8 +64,6 @@ decpcap_test: decpcap_test.cpp decpcap.o #-lefence -refresh.o: refresh.cpp refresh.h nethogs.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c refresh.cpp process.o: process.cpp process.h nethogs.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c process.cpp packet.o: packet.cpp packet.h nethogs.h diff --git a/main.cpp b/main.cpp index 70d6bde..70bbfcc 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include std::pair self_pipe = std::make_pair(-1, -1); +time_t last_refresh_time = 0; static void versiondisplay(void) { @@ -130,18 +131,18 @@ int main (int argc, char** argv) return 0; } } - - if ((!tracemode) && (!DEBUG)){ - init_ui(); - } - std::pair self_pipe = createSelfPipe(); - if( self_pipe.first == -1|| self_pipe.second == -1 ) + self_pipe = createSelfPipe(); + if( self_pipe.first == -1 || self_pipe.second == -1 ) { perror("Error creating pipe file descriptors\n"); return 0; } + if ((!tracemode) && (!DEBUG)){ + init_ui(); + } + if (NEEDROOT && (geteuid() != 0)) forceExit(false, "You need to be root to run NetHogs!"); @@ -209,9 +210,7 @@ int main (int argc, char** argv) pc_loop_fd_list.push_back(self_pipe.first); } - signal (SIGALRM, &alarm_cb); signal (SIGINT, &quit_cb); - alarm (refreshdelay); fprintf(stderr, "Waiting for first packet to arrive (see sourceforge.net bug 1019381)\n"); struct dpargs * userdata = (dpargs *) malloc (sizeof (struct dpargs)); @@ -241,18 +240,18 @@ int main (int argc, char** argv) current_handle = current_handle->next; } - - if (needrefresh) - { - if ((!DEBUG)&&(!tracemode)) + time_t const now = ::time(NULL); + if( last_refresh_time + refreshdelay <= now ) + { + last_refresh_time = now; + if ((!DEBUG)&&(!tracemode)) { // handle user input ui_tick(); } do_refresh(); - needrefresh = false; } - + //if not packets, do a select() until next packet if (!packets_read) { @@ -270,8 +269,8 @@ int main (int argc, char** argv) timeval timeout = {refreshdelay, 0}; if( select(nfds, &pc_loop_fd_set, 0, 0, &timeout) == -1 ) { - perror("error in select()\n"); - break; + //this happens on system signal + continue; } if( FD_ISSET(self_pipe.first, &pc_loop_fd_set) ) { diff --git a/nethogs.cpp b/nethogs.cpp index 0860742..fdc5914 100644 --- a/nethogs.cpp +++ b/nethogs.cpp @@ -46,7 +46,6 @@ extern "C" { #include "packet.h" #include "connection.h" #include "process.h" -#include "refresh.h" #include "devices.h" extern Process * unknownudp; @@ -57,7 +56,6 @@ unsigned refreshcount = 0; unsigned processlimit = 0; bool tracemode = false; bool bughuntmode = false; -bool needrefresh = false; // sort on sent or received? bool sortRecv = true; // viewMode: kb/s or total diff --git a/refresh.cpp b/refresh.cpp deleted file mode 100644 index 5195983..0000000 --- a/refresh.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * refresh.cpp - * - * Copyright (c) 2004 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. - * - */ - - -#include -#include -#include -#include "nethogs.h" - -extern bool needrefresh; -extern unsigned refreshdelay; - -void alarm_cb (int /*i*/) -{ - needrefresh = true; - //cout << "Setting needrefresh\n"; - - signal (SIGALRM, &alarm_cb); - alarm(refreshdelay); -} - diff --git a/refresh.h b/refresh.h deleted file mode 100644 index 152907f..0000000 --- a/refresh.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * refresh.h - * - * Copyright (c) 2004 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. - * - */ - - -void alarm_cb (int i); From 5595497ad8167da0683a6947e218b6c3e34a1325 Mon Sep 17 00:00:00 2001 From: Mohamed Boussaffa Date: Mon, 7 Mar 2016 06:54:28 +0800 Subject: [PATCH 4/5] cleaner code --- main.cpp | 129 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/main.cpp b/main.cpp index 70bbfcc..7609c95 100644 --- a/main.cpp +++ b/main.cpp @@ -2,8 +2,15 @@ #include #include -std::pair self_pipe = std::make_pair(-1, -1); -time_t last_refresh_time = 0; +//The self_pipe is used interrupt the select() in the main loop +static std::pair self_pipe = std::make_pair(-1, -1); +static time_t last_refresh_time = 0; + +//selectable file descriptionts for the main loop +static fd_set pc_loop_fd_set; +static std::vector pc_loop_fd_list; +static bool pc_loop_use_select = true; + static void versiondisplay(void) { @@ -48,7 +55,7 @@ void quit_cb (int /* i */) } } -std::pair createSelfPipe() +std::pair create_self_pipe() { int pfd[2]; if (pipe(pfd) == -1) @@ -63,6 +70,51 @@ std::pair createSelfPipe() return std::make_pair(pfd[0], pfd[1]); } +bool wait_for_next_trigger() +{ + if( pc_loop_use_select ) + { + FD_ZERO(&pc_loop_fd_set); + int nfds = 0; + for(std::vector::const_iterator it=pc_loop_fd_list.begin(); + it != pc_loop_fd_list.end(); ++it) + { + int const fd = *it; + nfds = std::max(nfds, *it + 1); + FD_SET(fd, &pc_loop_fd_set); + } + timeval timeout = {refreshdelay, 0}; + if( select(nfds, &pc_loop_fd_set, 0, 0, &timeout) != -1 ) + { + if( FD_ISSET(self_pipe.first, &pc_loop_fd_set) ) + { + return false; + } + } + } + else + { + // If select() not possible, pause to prevent 100% + usleep(1000); + } + return true; +} + + +void clean_up() +{ + //close file descriptors + for(std::vector::const_iterator it=pc_loop_fd_list.begin(); + it != pc_loop_fd_list.end(); ++it) + { + close(*it); + } + + procclean(); + if ((!tracemode) && (!DEBUG)) + exit_ui(); +} + int main (int argc, char** argv) { process_init(); @@ -131,13 +183,6 @@ int main (int argc, char** argv) return 0; } } - - self_pipe = createSelfPipe(); - if( self_pipe.first == -1 || self_pipe.second == -1 ) - { - perror("Error creating pipe file descriptors\n"); - return 0; - } if ((!tracemode) && (!DEBUG)){ init_ui(); @@ -145,13 +190,16 @@ int main (int argc, char** argv) if (NEEDROOT && (geteuid() != 0)) forceExit(false, "You need to be root to run NetHogs!"); - - fd_set pc_loop_fd_set; - std::vector pc_loop_fd_list; - bool pc_loop_use_select = true; - - if( pc_loop_use_select ) + + //use the Self-Pipe trick to interrupt the select() in the main loop + self_pipe = create_self_pipe(); + if( self_pipe.first == -1 || self_pipe.second == -1 ) { + forceExit(false, "Error creating pipe file descriptors\n"); + } + else + { + //add the self-pipe to allow interrupting select() pc_loop_fd_list.push_back(self_pipe.first); } @@ -194,6 +242,7 @@ int main (int argc, char** argv) { pc_loop_use_select = false; pc_loop_fd_list.clear(); + fprintf(stderr, "failed to get selectable_fd for %s\n", current_dev->name); } } } @@ -205,11 +254,6 @@ int main (int argc, char** argv) current_dev = current_dev->next; } - if( pc_loop_use_select ) - { - pc_loop_fd_list.push_back(self_pipe.first); - } - signal (SIGINT, &quit_cb); fprintf(stderr, "Waiting for first packet to arrive (see sourceforge.net bug 1019381)\n"); @@ -255,48 +299,15 @@ int main (int argc, char** argv) //if not packets, do a select() until next packet if (!packets_read) { - if( pc_loop_use_select ) + if( !wait_for_next_trigger() ) { - FD_ZERO(&pc_loop_fd_set); - int nfds = 0; - for(std::vector::const_iterator it=pc_loop_fd_list.begin(); - it != pc_loop_fd_list.end(); ++it) - { - int const fd = *it; - nfds = std::max(nfds, *it + 1); - FD_SET(fd, &pc_loop_fd_set); - } - timeval timeout = {refreshdelay, 0}; - if( select(nfds, &pc_loop_fd_set, 0, 0, &timeout) == -1 ) - { - //this happens on system signal - continue; - } - if( FD_ISSET(self_pipe.first, &pc_loop_fd_set) ) - { - std::cout << "exited by select\n"; - //exit the loop - break; - } - } - else - { - // If select() not possible, pause to prevent 100% - // Pause 10 milliseconds - usleep(1000); + //Exit the loop + break; } } } - //close file descriptors - for(std::vector::const_iterator it=pc_loop_fd_list.begin(); - it != pc_loop_fd_list.end(); ++it) - { - close(*it); - } - - procclean(); - if ((!tracemode) && (!DEBUG)) - exit_ui(); + //clean up + clean_up(); } From 014b9db3a56cec248f3302410929bba40ba75358 Mon Sep 17 00:00:00 2001 From: Mohamed Boussaffa Date: Mon, 7 Mar 2016 06:58:26 +0800 Subject: [PATCH 5/5] fixed spelling errors --- main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cpp b/main.cpp index 7609c95..52f1ee6 100644 --- a/main.cpp +++ b/main.cpp @@ -2,11 +2,11 @@ #include #include -//The self_pipe is used interrupt the select() in the main loop +//The self_pipe is used to interrupt the select() in the main loop static std::pair self_pipe = std::make_pair(-1, -1); static time_t last_refresh_time = 0; -//selectable file descriptionts for the main loop +//selectable file descriptors for the main loop static fd_set pc_loop_fd_set; static std::vector pc_loop_fd_list; static bool pc_loop_use_select = true;