diff --git a/Makefile b/Makefile index cced7aa..35638fe 100644 --- a/Makefile +++ b/Makefile @@ -16,10 +16,15 @@ 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 +OBJS=packet.o connection.o process.o decpcap.o cui.o inode2prog.o conninode.o devices.o NCURSES_LIBS?=-lncurses @@ -59,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 2a417d5..52f1ee6 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,16 @@ #include "nethogs.cpp" +#include +#include + +//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 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; + static void versiondisplay(void) { @@ -30,6 +42,79 @@ 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 ) + { + write(self_pipe.second, "x", 1); + } + else + { + exit(0); + } +} + +std::pair create_self_pipe() +{ + 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]); +} + +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(); @@ -102,9 +187,21 @@ int main (int argc, char** argv) if ((!tracemode) && (!DEBUG)){ init_ui(); } - + if (NEEDROOT && (geteuid() != 0)) forceExit(false, "You need to be root to run NetHogs!"); + + //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); + } char errbuf[PCAP_ERRBUF_SIZE]; @@ -132,6 +229,22 @@ 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(); + fprintf(stderr, "failed to get selectable_fd for %s\n", current_dev->name); + } + } } else { @@ -141,9 +254,7 @@ int main (int argc, char** argv) current_dev = current_dev->next; } - 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)); @@ -173,24 +284,30 @@ 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 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( !wait_for_next_trigger() ) + { + //Exit the loop + break; + } } } + + //clean up + clean_up(); } diff --git a/nethogs.cpp b/nethogs.cpp index 3f20df9..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 @@ -227,14 +225,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)){ 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);