diff --git a/.gitignore b/.gitignore index 60400ec..881125f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ decpcap_test TAGS *.o *~ +nethogs.project +libnethogs.so.* +libnethogs.a diff --git a/MakeApp.mk b/MakeApp.mk new file mode 100644 index 0000000..411d6b7 --- /dev/null +++ b/MakeApp.mk @@ -0,0 +1,81 @@ +VERSION := 0 +SUBVERSION := 8 +MINORVERSION := 2-SNAPSHOT + +#prefix := /usr +prefix := /usr/local + +sbin := $(prefix)/sbin +man8 := $(prefix)/share/man/man8 + +all: nethogs decpcap_test + +runtests: test + ./test + + +# nethogs_testsum + +CFLAGS?=-Wall -Wextra +CXXFLAGS?=-Wall -Wextra + +OBJS=packet.o connection.o process.o decpcap.o cui.o inode2prog.o conninode.o devices.o + +NCURSES_LIBS?=-lncurses + +.PHONY: tgz + +.PHONY: check uninstall +check: + @echo "Not implemented" + +install: nethogs nethogs.8 + install -d -m 755 $(DESTDIR)$(sbin) + install -m 755 nethogs $(DESTDIR)$(sbin) + install -d -m 755 $(DESTDIR)$(man8) + install -m 644 nethogs.8 $(DESTDIR)$(man8) + @echo + @echo "Installed nethogs to $(DESTDIR)$(sbin)" + @echo + @echo "You might have to add this directory to your PATH and/or refresh your shells' path cache with a command like 'hash -r'." + +uninstall: + rm $(DESTDIR)$(sbin)/nethogs + rm $(DESTDIR)$(man8)/nethogs.8 + +test: test.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) test.cpp -o test -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" + +nethogs: main.cpp nethogs.cpp $(OBJS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) main.cpp $(OBJS) -o nethogs -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" +nethogs_testsum: nethogs_testsum.cpp $(OBJS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) nethogs_testsum.cpp $(OBJS) -o nethogs_testsum -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" + +decpcap_test: decpcap_test.cpp decpcap.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) decpcap_test.cpp decpcap.o -o decpcap_test -lpcap -lm + +#-lefence + +process.o: process.cpp process.h nethogs.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c process.cpp +packet.o: packet.cpp packet.h nethogs.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c packet.cpp +connection.o: connection.cpp connection.h nethogs.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c connection.cpp +decpcap.o: decpcap.c decpcap.h + $(CC) $(CPPFLAGS) $(CFLAGS) -c decpcap.c +inode2prog.o: inode2prog.cpp inode2prog.h nethogs.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c inode2prog.cpp +conninode.o: conninode.cpp nethogs.h conninode.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c conninode.cpp +#devices.o: devices.cpp devices.h +# $(CXX) $(CXXFLAGS) -c devices.cpp +cui.o: cui.cpp cui.h nethogs.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c cui.cpp -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" + +.PHONY: clean +clean: + rm -f $(OBJS) + rm -f nethogs + rm -f test + rm -f decpcap_test diff --git a/MakeLib.mk b/MakeLib.mk new file mode 100644 index 0000000..c719eba --- /dev/null +++ b/MakeLib.mk @@ -0,0 +1,117 @@ +LIBVERSION := 0 +LIBSUBVERSION := 1 +LIBMINORVERSION := 0 + +LIBRARY=libnethogs.so +LIBNAME=$(LIBRARY).$(LIBVERSION).$(LIBSUBVERSION).$(LIBMINORVERSION) +SO_NAME=$(LIBRARY).$(LIBVERSION) + +prefix := /usr/local +libdir := $(prefix)/lib +incdir := $(prefix)/include + +all: $(LIBNAME) libnethogs.a + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + LDFLAGS:= -shared -Wl,-soname,$(SO_NAME) +else + LDFLAGS:= -shared -Wl,-install_name,$(SO_NAME) +endif + +CXXINCLUDES := +VISIBILITY=-fvisibility=hidden +ODIR_BASE := obj + +ifeq ($(DEBUG),1) + # Debug mode options + $(info Bulding debug version) + ODIR:=$(ODIR_BASE)/lib/debug + CFLAGS?=-Wall -Wextra -O0 -g -fPIC $(VISIBILITY) + CXXFLAGS?=-Wall -Wextra -O0 -g -fPIC $(VISIBILITY) $(CXXINCLUDES) +else + # Release mode options + ODIR:=$(ODIR_BASE)/lib/release + CFLAGS?=-Wall -Wextra -O3 -fPIC $(VISIBILITY) + CXXFLAGS?=-Wall -Wextra -O3 -fPIC $(VISIBILITY) $(CXXINCLUDES) +endif + +OBJ_NAMES= libnethogs.o packet.o connection.o process.o decpcap.o inode2prog.o conninode.o devices.o +OBJS=$(addprefix $(ODIR)/,$(OBJ_NAMES)) + +#$(info $(OBJS)) + +.PHONY: tgz + +.PHONY: uninstall + +install: $(LIBNAME) + install -d -m 755 $(DESTDIR)$(libdir) + install -m 755 $(LIBNAME) $(DESTDIR)$(libdir) + @echo "Installed $(LIBNAME) to $(DESTDIR)$(libdir)" + ldconfig + +install_dev: install + @ln -f -s $(DESTDIR)$(libdir)/$(LIBNAME) $(DESTDIR)$(libdir)/$(LIBRARY) + install -m 755 libnethogs.a $(DESTDIR)$(libdir) + @echo "Installed libnethogs.a to $(DESTDIR)$(libdir)" + install -d -m 755 $(DESTDIR)$(incdir) + install -m 755 libnethogs.h $(DESTDIR)$(incdir) + @echo "Installed libnethogs.h to $(DESTDIR)$(incdir)" + ldconfig + +uninstall: + rm -f $(DESTDIR)$(libdir)/$(LIBNAME) + rm -f $(DESTDIR)$(libdir)/$(LIBRARY) + rm -f $(DESTDIR)$(libdir)/libnethogs.a + rm -f $(DESTDIR)$(incdir)/libnethogs.h + ldconfig + +$(LIBNAME): $(OBJS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ -lpcap + +libnethogs.a: $(OBJS) + $(AR) rcs $@ $(OBJS) + +#-lefence + +$(ODIR)/process.o: process.cpp process.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c process.cpp + +$(ODIR)/packet.o: packet.cpp packet.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c packet.cpp + +$(ODIR)/connection.o: connection.cpp connection.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c connection.cpp + +$(ODIR)/decpcap.o: decpcap.c decpcap.h + @mkdir -p $(ODIR) + $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c decpcap.c + +$(ODIR)/inode2prog.o: inode2prog.cpp inode2prog.h nethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c inode2prog.cpp + +$(ODIR)/conninode.o: conninode.cpp nethogs.h conninode.h + @mkdir -p $(ODIR) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c conninode.cpp + +$(ODIR)/devices.o: devices.cpp devices.h + @mkdir -p $(ODIR) + $(CXX) $(CXXFLAGS) -o $@ -c devices.cpp + +$(ODIR)/libnethogs.o: libnethogs.cpp libnethogs.h + @mkdir -p $(ODIR) + $(CXX) $(CXXFLAGS) -o $@ -c libnethogs.cpp -DVERSION=\"$(LIBVERSION)\" -DSUBVERSION=\"$(LIBSUBVERSION)\" -DMINORVERSION=\"$(LIBMINORVERSION)\" + +.PHONY: clean +clean: + rm -f $(OBJS) + rm -f $(LIBNAME) + rm -f libnethogs.a + mkdir -p $(ODIR) + rmdir -p --ignore-fail-on-non-empty $(ODIR) + diff --git a/Makefile b/Makefile index 35638fe..766c960 100644 --- a/Makefile +++ b/Makefile @@ -1,89 +1,36 @@ -VERSION := 0 -SUBVERSION := 8 -MINORVERSION := 2-SNAPSHOT - -#prefix := /usr -prefix := /usr/local - -sbin := $(prefix)/sbin -man8 := $(prefix)/share/man/man8 - all: nethogs decpcap_test + $(MAKE) -f MakeApp.mk $@ + $(MAKE) -f MakeLib.mk $@ runtests: test ./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 decpcap.o cui.o inode2prog.o conninode.o devices.o - -NCURSES_LIBS?=-lncurses - .PHONY: tgz - tgz: clean cd .. ; tar czvf nethogs-$(VERSION).$(SUBVERSION).$(MINORVERSION).tar.gz --exclude-vcs nethogs/* -.PHONY: check uninstall +.PHONY: check: - @echo "Not implemented" + $(MAKE) -f MakeApp.mk $@ -install: nethogs nethogs.8 - install -d -m 755 $(DESTDIR)$(sbin) - install -m 755 nethogs $(DESTDIR)$(sbin) - install -d -m 755 $(DESTDIR)$(man8) - install -m 644 nethogs.8 $(DESTDIR)$(man8) - @echo - @echo "Installed nethogs to $(DESTDIR)$(sbin)" - @echo - @echo "You might have to add this directory to your PATH and/or refresh your shells' path cache with a command like 'hash -r'." +install: + $(MAKE) -f MakeApp.mk $@ + $(MAKE) -f MakeLib.mk $@ + +install_dev: + $(MAKE) -f MakeLib.mk $@ uninstall: - rm $(DESTDIR)$(sbin)/nethogs - rm $(DESTDIR)$(man8)/nethogs.8 - -test: test.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) test.cpp -o test -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" - -nethogs: main.cpp nethogs.cpp $(OBJS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) main.cpp $(OBJS) -o nethogs -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -nethogs_testsum: nethogs_testsum.cpp $(OBJS) - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) nethogs_testsum.cpp $(OBJS) -o nethogs_testsum -lpcap -lm ${NCURSES_LIBS} -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" - -decpcap_test: decpcap_test.cpp decpcap.o - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) decpcap_test.cpp decpcap.o -o decpcap_test -lpcap -lm - -#-lefence - -process.o: process.cpp process.h nethogs.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c process.cpp -packet.o: packet.cpp packet.h nethogs.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c packet.cpp -connection.o: connection.cpp connection.h nethogs.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c connection.cpp -decpcap.o: decpcap.c decpcap.h - $(CC) $(CPPFLAGS) $(CFLAGS) -c decpcap.c -inode2prog.o: inode2prog.cpp inode2prog.h nethogs.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c inode2prog.cpp -conninode.o: conninode.cpp nethogs.h conninode.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c conninode.cpp -#devices.o: devices.cpp devices.h -# $(CXX) $(CXXFLAGS) -c devices.cpp -cui.o: cui.cpp cui.h nethogs.h - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c cui.cpp -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" - + $(MAKE) -f MakeApp.mk $@ + $(MAKE) -f MakeLib.mk $@ + +nethogs: + $(MAKE) -f MakeApp.mk $@ + +decpcap_test: + $(MAKE) -f MakeApp.mk $@ + .PHONY: clean clean: - rm -f $(OBJS) - rm -f nethogs - rm -f test - rm -f decpcap_test + $(MAKE) -f MakeApp.mk $@ + $(MAKE) -f MakeLib.mk $@ diff --git a/cui.cpp b/cui.cpp index df8fd06..ca7aa90 100644 --- a/cui.cpp +++ b/cui.cpp @@ -33,7 +33,6 @@ #include "nethogs.h" #include "process.h" - std::string * caption; extern const char version[]; extern ProcList * processes; @@ -277,106 +276,6 @@ void ui_tick () } } -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; -} - void show_trace(Line * lines[], int nproc) { std::cout << "\nRefreshing:\n"; @@ -512,22 +411,22 @@ void do_refresh() if (viewMode == VIEWMODE_KBPS) { //std::cout << "kbps viemode" << std::endl; - getkbps (curproc->getVal(), &value_recv, &value_sent); + curproc->getVal()->getkbps (&value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_KB) { //std::cout << "total viemode" << std::endl; - gettotalkb(curproc->getVal(), &value_recv, &value_sent); + curproc->getVal()->gettotalkb(&value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_MB) { //std::cout << "total viemode" << std::endl; - gettotalmb(curproc->getVal(), &value_recv, &value_sent); + curproc->getVal()->gettotalmb(&value_recv, &value_sent); } else if (viewMode == VIEWMODE_TOTAL_B) { //std::cout << "total viemode" << std::endl; - gettotalb(curproc->getVal(), &value_recv, &value_sent); + curproc->getVal()->gettotalb(&value_recv, &value_sent); } else { diff --git a/libnethogs.cpp b/libnethogs.cpp new file mode 100644 index 0000000..4fd826e --- /dev/null +++ b/libnethogs.cpp @@ -0,0 +1,371 @@ +extern "C" +{ + #include "libnethogs.h" +} + +#include "nethogs.cpp" +#include +#include +#include +#include +#include +#include +#include + +////////////////////////////// +extern ProcList * processes; +extern Process * unknowntcp; +extern Process * unknownudp; +extern Process * unknownip; +////////////////////////////// + +//The self_pipe is used to interrupt the select() in the main loop +static std::pair self_pipe = std::make_pair(-1, -1); + +static bool monitor_run_flag = false; +typedef std::map NethogsRecordMap; +static NethogsRecordMap monitor_record_map; + +static int monitor_refresh_delay = 1; +static time_t monitor_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 handle * handles = NULL; + +static 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]); +} + +static 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 = {monitor_refresh_delay, 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; +} + +static int nethogsmonitor_init() +{ + process_init(); + + device * devices = get_default_devices(); + if ( devices == NULL ) + { + std::cerr << "Not devices to monitor" << std::endl; + return NETHOGS_STATUS_NO_DEVICE; + } + + device * current_dev = devices; + + bool promiscuous = false; + + int nb_devices = 0; + int nb_failed_devices = 0; + + while (current_dev != NULL) + { + ++nb_devices; + + if( !getLocal(current_dev->name, false) ) + { + std::cerr << "getifaddrs failed while establishing local IP." << std::endl; + ++nb_failed_devices; + continue; + } + + char errbuf[PCAP_ERRBUF_SIZE]; + dp_handle * newhandle = dp_open_live(current_dev->name, BUFSIZ, promiscuous, 100, errbuf); + if (newhandle != NULL) + { + dp_addcb (newhandle, dp_packet_ip, process_ip); + dp_addcb (newhandle, dp_packet_ip6, process_ip6); + dp_addcb (newhandle, dp_packet_tcp, process_tcp); + dp_addcb (newhandle, dp_packet_udp, process_udp); + + /* The following code solves sf.net bug 1019381, but is only available + * in newer versions (from 0.8 it seems) of libpcap + * + * update: version 0.7.2, which is in debian stable now, should be ok + * also. + */ + if (dp_setnonblock (newhandle, 1, errbuf) == -1) + { + 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 + { + fprintf(stderr, "ERROR: opening handler for device %s: %s\n", current_dev->name, strerror(errno)); + ++nb_failed_devices; + } + + current_dev = current_dev->next; + } + + if(nb_devices == nb_failed_devices) + { + return NETHOGS_STATUS_FAILURE; + } + + //use the Self-Pipe trick to interrupt the select() in the main loop + if( pc_loop_use_select ) + { + self_pipe = create_self_pipe(); + if( self_pipe.first == -1 || self_pipe.second == -1 ) + { + std::cerr << "Error creating pipe file descriptors\n"; + pc_loop_use_select = false; + } + else + { + pc_loop_fd_list.push_back(self_pipe.first); + } + } + + return NETHOGS_STATUS_OK; +} + +static void nethogsmonitor_handle_update(NethogsMonitorCallback cb) +{ + refreshconninode(); + refreshcount++; + + ProcList * curproc = processes; + ProcList * previousproc = NULL; + int nproc = processes->size(); + + 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"; + + NethogsRecordMap::iterator it = monitor_record_map.find(curproc); + if( it != monitor_record_map.end() ) + { + NethogsMonitorRecord& data = it->second; + (*cb)(NETHOGS_APP_ACTION_REMOVE, &data); + monitor_record_map.erase(curproc); + } + + 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 + { + const u_int32_t uid = curproc->getVal()->getUid(); + u_int32_t sent_bytes; + u_int32_t recv_bytes; + float sent_kbs; + float recv_kbs; + curproc->getVal()->getkbps (&recv_kbs, &sent_kbs); + curproc->getVal()->gettotal (&recv_bytes, &sent_bytes); + + //notify update + bool const new_data = (monitor_record_map.find(curproc) == monitor_record_map.end()); + NethogsMonitorRecord &data = monitor_record_map[curproc]; + + bool data_change = false; + if( new_data ) + { + data_change = true; + static int record_id = 0; + ++record_id; + memset(&data, 0, sizeof(data)); + data.record_id = record_id; + data.name = curproc->getVal()->name; + data.pid = curproc->getVal()->pid; + } + + data.device_name = curproc->getVal()->devicename; + + #define NHM_UPDATE_ONE_FIELD(TO,FROM) if((TO)!=(FROM)) { TO = FROM; data_change = true; } + + NHM_UPDATE_ONE_FIELD( data.uid, uid ) + NHM_UPDATE_ONE_FIELD( data.sent_bytes, sent_bytes ) + NHM_UPDATE_ONE_FIELD( data.recv_bytes, recv_bytes ) + NHM_UPDATE_ONE_FIELD( data.sent_kbs, sent_kbs ) + NHM_UPDATE_ONE_FIELD( data.recv_kbs, recv_kbs ) + + #undef NHM_UPDATE_ONE_FIELD + + if( data_change ) + { + (*cb)(NETHOGS_APP_ACTION_SET, &data); + } + + //next + previousproc = curproc; + curproc = curproc->next; + } + } +} + +static void nethogsmonitor_clean_up() +{ + //clean up + handle * current_handle = handles; + while (current_handle != NULL) + { + pcap_close(current_handle->content->pcap_handle); + current_handle = current_handle->next; + } + + //close file descriptors + for(std::vector::const_iterator it=pc_loop_fd_list.begin(); + it != pc_loop_fd_list.end(); ++it) + { + close(*it); + } + + procclean(); +} + +int nethogsmonitor_loop(NethogsMonitorCallback cb) +{ + if( monitor_run_flag ) + { + return NETHOGS_STATUS_FAILURE; + } + + int return_value = nethogsmonitor_init(); + if( return_value != NETHOGS_STATUS_OK ) + { + return return_value; + } + + monitor_run_flag = true; + + struct dpargs * userdata = (dpargs *) malloc (sizeof (struct dpargs)); + + // Main loop + while (monitor_run_flag) + { + bool packets_read = false; + + handle * current_handle = handles; + while (current_handle != NULL) + { + userdata->device = current_handle->devicename; + userdata->sa_family = AF_UNSPEC; + int retval = dp_dispatch (current_handle->content, -1, (u_char *)userdata, sizeof (struct dpargs)); + if (retval < 0) + { + std::cerr << "Error dispatching: " << retval << std::endl; + } + else if (retval != 0) + { + packets_read = true; + } + else + { + gettimeofday(&curtime, NULL); + } + current_handle = current_handle->next; + } + + time_t const now = ::time(NULL); + if( monitor_last_refresh_time + monitor_refresh_delay <= now ) + { + monitor_last_refresh_time = now; + nethogsmonitor_handle_update(cb); + } + + if (!packets_read) + { + if( !wait_for_next_trigger() ) + { + break; + } + } + } + + nethogsmonitor_clean_up(); + + return NETHOGS_STATUS_OK; +} + +void nethogsmonitor_breakloop() +{ + monitor_run_flag = false; + write(self_pipe.second, "x", 1); +} diff --git a/libnethogs.h b/libnethogs.h new file mode 100644 index 0000000..afde2a4 --- /dev/null +++ b/libnethogs.h @@ -0,0 +1,67 @@ +#ifndef LIBNETHOGS_H_ +#define LIBNETHOGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define NETHOGS_DSO_VISIBLE __attribute__ ((visibility ("default"))) +#define NETHOGS_DSO_HIDDEN __attribute__ ((visibility ("hidden"))) + +#define NETHOGS_APP_ACTION_SET 1 +#define NETHOGS_APP_ACTION_REMOVE 2 + +#define NETHOGS_STATUS_OK 0 +#define NETHOGS_STATUS_FAILURE 1 //generic error +#define NETHOGS_STATUS_NO_DEVICE 2 //no device foundr + +typedef struct NethogsMonitorRecord +{ + int record_id; + const char* name; + int pid; + uint32_t uid; + const char* device_name; + uint32_t sent_bytes; + uint32_t recv_bytes; + float sent_kbs; + float recv_kbs; +} NethogsMonitorRecord; + + +/** + * @brief Defines a callback to handle updates about applications + * @param action NETHOGS_APP_ACTION_SET if data is beeing added or updated, + * NETHOGS_APP_ACTION_REMOVE if data is beeing removed. + * the record_id member is used to uniquely identify the data beeing update or removed. + * @param data a pointer to an application usage data. the pointer remains valid until + * the callback is called with NETHOGS_APP_ACTION_REMOVE for the same pointer. + * the user should not modify the content of the structure pointed by data. + */ +typedef void(*NethogsMonitorCallback)(int action, NethogsMonitorRecord const* data); + +/** + * @brief Enter the process monitoring loop and reports updates using the + * callback provided as parameter. + * This call will block until nethogsmonitor_breakloop() is called or a failure occurs. + * @param cb A pointer to a callback function following the NethogsMonitorCallback definition + */ + +NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb); + +/** + * @brief Makes the call to nethogsmonitor_loop return. + */ +NETHOGS_DSO_VISIBLE void nethogsmonitor_breakloop(); + +#undef NETHOGS_DSO_VISIBLE +#undef NETHOGS_DSO_HIDDEN + +#ifdef __cplusplus +} +#endif + +#endif // LIBNETHOGS_H_ diff --git a/main.cpp b/main.cpp index 52f1ee6..899df40 100644 --- a/main.cpp +++ b/main.cpp @@ -55,6 +55,24 @@ void quit_cb (int /* i */) } } +void forceExit(bool success, const char *msg, ...) +{ + if ((!tracemode)&&(!DEBUG)){ + exit_ui(); + } + + va_list argp; + va_start(argp, msg); + vfprintf(stderr, msg, argp); + va_end(argp); + std::cerr << std::endl; + + if (success) + exit(EXIT_SUCCESS); + else + exit(EXIT_FAILURE); +} + std::pair create_self_pipe() { int pfd[2]; @@ -208,7 +226,11 @@ int main (int argc, char** argv) handle * handles = NULL; device * current_dev = devices; while (current_dev != NULL) { - getLocal(current_dev->name, tracemode); + + if( !getLocal(current_dev->name, tracemode) ) + { + forceExit(false, "getifaddrs failed while establishing local IP."); + } dp_handle * newhandle = dp_open_live(current_dev->name, BUFSIZ, promisc, 100, errbuf); if (newhandle != NULL) diff --git a/nethogs.cpp b/nethogs.cpp index fdc5914..d2e134f 100644 --- a/nethogs.cpp +++ b/nethogs.cpp @@ -225,24 +225,6 @@ int process_ip6 (u_char * userdata, const dp_header * /* header */, const u_char return false; } -void forceExit(bool success, const char *msg, ...) -{ - if ((!tracemode)&&(!DEBUG)){ - exit_ui(); - } - - va_list argp; - va_start(argp, msg); - vfprintf(stderr, msg, argp); - va_end(argp); - std::cerr << std::endl; - - if (success) - exit(EXIT_SUCCESS); - else - exit(EXIT_FAILURE); -} - class handle { public: handle (dp_handle * m_handle, const char * m_devicename = NULL, diff --git a/packet.cpp b/packet.cpp index fc69a9c..7e37aad 100644 --- a/packet.cpp +++ b/packet.cpp @@ -48,11 +48,11 @@ local_addr * local_addrs = NULL; * uses getifaddrs to get addresses of this device, and adds them to the * local_addrs-list. */ -void getLocal (const char *device, bool tracemode) +bool getLocal (const char *device, bool tracemode) { struct ifaddrs *ifaddr, *ifa; if(getifaddrs(&ifaddr) == -1) { - forceExit(false, "getifaddrs failed while establishing local IP."); + return false; } for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { @@ -81,6 +81,7 @@ void getLocal (const char *device, bool tracemode) } } } + return true; } typedef u_int32_t tcp_seq; diff --git a/packet.h b/packet.h index 7b28eec..dc497d7 100644 --- a/packet.h +++ b/packet.h @@ -39,7 +39,7 @@ enum direction { /* To initialise this module, call getLocal with the currently * monitored device (e.g. "eth0:1") */ -void getLocal (const char *device, bool tracemode); +bool getLocal (const char *device, bool tracemode); class Packet { diff --git a/process.cpp b/process.cpp index 8f55cdf..0e92ca9 100644 --- a/process.cpp +++ b/process.cpp @@ -43,6 +43,8 @@ extern local_addr * local_addrs; +extern timeval curtime; + /* * connection-inode table. takes information from /proc/net/tcp. * key contains source ip, source port, destination ip, destination @@ -73,6 +75,21 @@ ProcList * processes; std::map unknownprocs; +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; +} + + void process_init () { unknowntcp = new Process (0, "", "unknown TCP"); @@ -98,6 +115,89 @@ int Process::getLastPacket() return lastpacket; } +/** Get the kb/s values for this process */ +void Process::getkbps (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 = this->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 == this->connections) + this->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 Process::gettotal( u_int32_t * recvd, u_int32_t * sent) +{ + u_int32_t sum_sent = 0, sum_recv = 0; + ConnList * curconn = this->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 Process::gettotalmb(float * recvd, float * sent) +{ + u_int32_t sum_sent = 0, sum_recv = 0; + gettotal(&sum_recv, &sum_sent); + *recvd = tomb(sum_recv); + *sent = tomb(sum_sent); +} + +/** get total values for this process */ +void Process::gettotalkb(float * recvd, float * sent) +{ + u_int32_t sum_sent = 0, sum_recv = 0; + gettotal(&sum_recv, &sum_sent); + *recvd = tokb(sum_recv); + *sent = tokb(sum_sent); +} + +void Process::gettotalb(float * recvd, float * sent) +{ + u_int32_t sum_sent = 0, sum_recv = 0; + gettotal(&sum_recv, &sum_sent); + //std::cout << "Total sent: " << sum_sent << std::endl; + *sent = sum_sent; + *recvd = sum_recv; +} + + Process * findProcess (struct prg_node * node) { ProcList * current = processes; diff --git a/process.h b/process.h index 728649b..3783928 100644 --- a/process.h +++ b/process.h @@ -95,6 +95,12 @@ public: } int getLastPacket (); + void gettotal( u_int32_t * recvd, u_int32_t * sent); + void getkbps (float * recvd, float * sent); + void gettotalmb(float * recvd, float * sent); + void gettotalkb(float * recvd, float * sent); + void gettotalb (float * recvd, float * sent); + char * name; const char * devicename; int pid;