aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEugeni Dodonov <eugeni@mandriva.com>2009-09-29 14:18:24 -0300
committerEugeni Dodonov <eugeni@mandriva.com>2009-09-29 14:18:24 -0300
commite6aa6bf53cb954f1a937f6cafc79e4c60c172cd3 (patch)
treed3bc3b61d6e44a39a7c24ab4b970810e6ecb9e66 /src
parent9795b7728134d4d939c0f364f37ac1a378de434c (diff)
downloadnet_monitor-e6aa6bf53cb954f1a937f6cafc79e4c60c172cd3.tar
net_monitor-e6aa6bf53cb954f1a937f6cafc79e4c60c172cd3.tar.gz
net_monitor-e6aa6bf53cb954f1a937f6cafc79e4c60c172cd3.tar.bz2
net_monitor-e6aa6bf53cb954f1a937f6cafc79e4c60c172cd3.tar.xz
net_monitor-e6aa6bf53cb954f1a937f6cafc79e4c60c172cd3.zip
converted to python module format
Diffstat (limited to 'src')
-rw-r--r--src/__init__.py0
-rw-r--r--src/_native.c229
-rwxr-xr-xsrc/net_monitor595
-rw-r--r--src/wifi.py75
4 files changed, 899 insertions, 0 deletions
diff --git a/src/__init__.py b/src/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/__init__.py
diff --git a/src/_native.c b/src/_native.c
new file mode 100644
index 0000000..5524c66
--- /dev/null
+++ b/src/_native.c
@@ -0,0 +1,229 @@
+#include <Python.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef __user
+#define __user
+#endif
+#include <sys/types.h>
+#include <linux/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <wireless.h>
+
+/************************ CONSTANTS & MACROS ************************/
+
+/*
+ * Constants fof WE-9->15
+ */
+#define IW15_MAX_FREQUENCIES 16
+#define IW15_MAX_BITRATES 8
+#define IW15_MAX_TXPOWER 8
+#define IW15_MAX_ENCODING_SIZES 8
+#define IW15_MAX_SPY 8
+#define IW15_MAX_AP 8
+
+/*
+ * Struct iw_range up to WE-15
+ */
+struct iw15_range
+{
+ __u32 throughput;
+ __u32 min_nwid;
+ __u32 max_nwid;
+ __u16 num_channels;
+ __u8 num_frequency;
+ struct iw_freq freq[IW15_MAX_FREQUENCIES];
+ __s32 sensitivity;
+ struct iw_quality max_qual;
+ __u8 num_bitrates;
+ __s32 bitrate[IW15_MAX_BITRATES];
+ __s32 min_rts;
+ __s32 max_rts;
+ __s32 min_frag;
+ __s32 max_frag;
+ __s32 min_pmp;
+ __s32 max_pmp;
+ __s32 min_pmt;
+ __s32 max_pmt;
+ __u16 pmp_flags;
+ __u16 pmt_flags;
+ __u16 pm_capa;
+ __u16 encoding_size[IW15_MAX_ENCODING_SIZES];
+ __u8 num_encoding_sizes;
+ __u8 max_encoding_tokens;
+ __u16 txpower_capa;
+ __u8 num_txpower;
+ __s32 txpower[IW15_MAX_TXPOWER];
+ __u8 we_version_compiled;
+ __u8 we_version_source;
+ __u16 retry_capa;
+ __u16 retry_flags;
+ __u16 r_time_flags;
+ __s32 min_retry;
+ __s32 max_retry;
+ __s32 min_r_time;
+ __s32 max_r_time;
+ struct iw_quality avg_qual;
+};
+
+/*
+ * Union for all the versions of iwrange.
+ * Fortunately, I mostly only add fields at the end, and big-bang
+ * reorganisations are few.
+ */
+union iw_range_raw
+{
+ struct iw15_range range15; /* WE 9->15 */
+ struct iw_range range; /* WE 16->current */
+};
+
+/*
+ * Offsets in iw_range struct
+ */
+#define iwr15_off(f) ( ((char *) &(((struct iw15_range *) NULL)->f)) - \
+ (char *) NULL)
+#define iwr_off(f) ( ((char *) &(((struct iw_range *) NULL)->f)) - \
+ (char *) NULL)
+
+typedef struct iw_range iwrange;
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper to extract some Wireless Parameter out of the driver
+ */
+static inline int
+iw_get_ext(int skfd, /* Socket to the kernel */
+ const char * ifname, /* Device name */
+ int request, /* WE ID */
+ struct iwreq * pwrq) /* Fixed part of the request */
+{
+ /* Set device name */
+ strncpy(pwrq->ifr_name, ifname, IFNAMSIZ);
+ /* Do the request */
+ return(ioctl(skfd, request, pwrq));
+}
+/*------------------------------------------------------------------*/
+/*
+ * Get the range information out of the driver
+ */
+int
+iw_get_range_info(int skfd, const char *ifname, iwrange * range)
+{
+ struct iwreq wrq;
+ char buffer[sizeof(iwrange) * 2]; /* Large enough */
+ union iw_range_raw * range_raw;
+
+ /* Cleanup */
+ bzero(buffer, sizeof(buffer));
+
+ wrq.u.data.pointer = (caddr_t) buffer;
+ wrq.u.data.length = sizeof(buffer);
+ wrq.u.data.flags = 0;
+ if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
+ return(-1);
+
+ /* Point to the buffer */
+ range_raw = (union iw_range_raw *) buffer;
+
+ /* For new versions, we can check the version directly, for old versions
+ * we use magic. 300 bytes is a also magic number, don't touch... */
+ if(wrq.u.data.length < 300)
+ {
+ /* That's v10 or earlier. Ouch ! Let's make a guess...*/
+ range_raw->range.we_version_compiled = 9;
+ }
+
+ /* Check how it needs to be processed */
+ if(range_raw->range.we_version_compiled > 15)
+ {
+ /* This is our native format, that's easy... */
+ /* Copy stuff at the right place, ignore extra */
+ memcpy((char *) range, buffer, sizeof(iwrange));
+ }
+ else
+ {
+ /* Zero unknown fields */
+ bzero((char *) range, sizeof(struct iw_range));
+
+ /* Initial part unmoved */
+ memcpy((char *) range,
+ buffer,
+ iwr15_off(num_channels));
+ /* Frequencies pushed futher down towards the end */
+ memcpy((char *) range + iwr_off(num_channels),
+ buffer + iwr15_off(num_channels),
+ iwr15_off(sensitivity) - iwr15_off(num_channels));
+ /* This one moved up */
+ memcpy((char *) range + iwr_off(sensitivity),
+ buffer + iwr15_off(sensitivity),
+ iwr15_off(num_bitrates) - iwr15_off(sensitivity));
+ /* This one goes after avg_qual */
+ memcpy((char *) range + iwr_off(num_bitrates),
+ buffer + iwr15_off(num_bitrates),
+ iwr15_off(min_rts) - iwr15_off(num_bitrates));
+ /* Number of bitrates has changed, put it after */
+ memcpy((char *) range + iwr_off(min_rts),
+ buffer + iwr15_off(min_rts),
+ iwr15_off(txpower_capa) - iwr15_off(min_rts));
+ /* Added encoding_login_index, put it after */
+ memcpy((char *) range + iwr_off(txpower_capa),
+ buffer + iwr15_off(txpower_capa),
+ iwr15_off(txpower) - iwr15_off(txpower_capa));
+ /* Hum... That's an unexpected glitch. Bummer. */
+ memcpy((char *) range + iwr_off(txpower),
+ buffer + iwr15_off(txpower),
+ iwr15_off(avg_qual) - iwr15_off(txpower));
+ /* Avg qual moved up next to max_qual */
+ memcpy((char *) range + iwr_off(avg_qual),
+ buffer + iwr15_off(avg_qual),
+ sizeof(struct iw_quality));
+ }
+
+ return(0);
+}
+
+static PyObject *
+ wifi_get_max_quality(PyObject *self, PyObject *args)
+{
+ const char *iface;
+ int max_quality;
+ int fd, err;
+ struct iw_range range;
+
+ if (!PyArg_ParseTuple(args, "s", &iface))
+ return NULL;
+
+ fd = socket (PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ fprintf (stderr, "couldn't open socket\n");
+ return NULL;
+ }
+
+ err = iw_get_range_info(fd, iface, &range);
+ close (fd);
+
+ if (err < 0) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ return NULL;
+ }
+ max_quality = range.max_qual.qual;
+ return Py_BuildValue("i", max_quality);
+}
+
+/* python module details */
+static PyMethodDef net_monitor_Methods[] = {
+ {"wifi_get_max_quality", wifi_get_max_quality, METH_VARARGS,
+ "Find maximum quality value for a wireless interface."},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+PyMODINIT_FUNC
+init_native(void)
+{
+ (void) Py_InitModule("_native", net_monitor_Methods);
+}
+
diff --git a/src/net_monitor b/src/net_monitor
new file mode 100755
index 0000000..fc05f2d
--- /dev/null
+++ b/src/net_monitor
@@ -0,0 +1,595 @@
+#!/usr/bin/python
+
+import gobject
+import gtk
+import pango
+
+import gc
+import os
+from stat import *
+import datetime
+import getopt
+import sys
+import traceback
+
+# for address
+import socket
+import fcntl
+import struct
+
+import time
+
+import textwrap
+
+# localization
+import gettext
+try:
+ gettext.install("msec")
+except IOError:
+ _ = str
+
+ifaces = {}
+HISTOGRAM_SIZE=50
+
+def get_status(ifname):
+ try:
+ fd = open("/sys/class/net/%s/operstate" % ifname)
+ status = fd.readline().strip()
+ fd.close()
+ except:
+ status="unknown"
+ if status == "unknown":
+ # pretty-format interface status
+ status = _("Unknown")
+ return status
+
+def readwireless():
+ """Check if device is wireless and get its details if necessary"""
+ try:
+ with open("/proc/net/wireless") as fd:
+ ifaces = fd.readlines()[2:]
+ for line in ifaces:
+ line = line.strip()
+ if not line:
+ continue
+ iface, params = line.split(":", 1)
+ iface = iface.strip()
+ params = params.replace(".", "").split()
+ return {}
+ except:
+ # something bad happened
+ traceback.print_exc()
+ return {}
+
+def get_address(ifname):
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ mac=_("No physical address")
+ # ip address
+ try:
+ addr=socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
+ except:
+ addr=_("No address assigned")
+ # mac address
+ try:
+ mac_struct=fcntl.ioctl( s.fileno(), 0x8927, struct.pack('256s', ifname[:15]))[18:24]
+ mac=":".join(["%02x" % ord(char) for char in mac_struct])
+ except:
+ addr=_("No address assigned")
+ # addr, mac
+ return addr, mac
+
+def readnet():
+ """Reads values from /proc/net/dev"""
+ net = {}
+ data = open("/proc/net/dev").readlines()[2:]
+ for l in data:
+ dev, vals = l.split(":")
+ dev = dev.strip()
+ vals = vals.split()
+ net[dev] = vals
+ return net
+
+def get_traffic(iface, net=None):
+ if not net:
+ net = readnet()
+ if iface in net:
+ bytes_in = int(net[iface][0])
+ bytes_out = int(net[iface][8])
+ else:
+ bytes_in = 0
+ bytes_out = 0
+ return bytes_in, bytes_out
+
+def format_size(size):
+ """Pretty-Formats size"""
+ return size
+
+# borrowed from gnome-network-monitor 0.9.1 (gnetworkmonitor.sourceforge.net)
+class LoadGraph:
+ """
+ This class is able do display a nicely formatted graph if interface
+ bandwidth load.
+ """
+ # we don't want to allow the window to shrink below this height
+ min_height = 70
+ padding = { "left" : 50, "right" : 10, "top" : 10, "bottom" : 10 }
+ colors = ( "bg", "bg_outer", "in", "out" )
+
+ def __init__(self, widget, hist, size):
+ """
+ widget => GtkDrawingArea we paint into
+ hist => a list of integers containing the hist of incoming traffic
+ width, height => initial size of the GtkDrawingArea widget
+ """
+ # set the minimum height of the widget
+ widget.set_size_request(-1, 70)
+
+ # the object holding the history and its size
+ self.__hist = hist
+ self.__size = size
+
+ # strings holding 0, middle and max values for displayed graph
+ self.__str_min = "0"
+ self.__str_mid = "" # gets computed later
+ self.__str_max = "" # gets computer later
+
+ # size of the GtkDrawingArea
+ self.__rect = self.__inner = gtk.gdk.Rectangle()
+ self.maxval = 0 # maximum value in the history
+ self.__mesh_x = self.__mesh_y = 0 # distance in pixels between items
+ self.__get_max()
+ self.__on_size(widget.get_allocation())
+
+ # save reference to the widget we paint into
+ self.__widget = widget
+
+ # lists holding bandwidth history mapped to actual coordinates
+ self.__in = list()
+ self.__out = list()
+
+ self.__colors = dict()
+ self.set_color(bg=(0, 0, 0), fg_in=(255, 0, 0), fg_out=(0, 255, 0))
+ self.__context = None
+
+ def __set_context_color(self, con, col_tuple):
+ """ Cleaner might be to extend the context class, but who cares? """
+ con.set_source_rgb(col_tuple[0], col_tuple[1], col_tuple[2])
+
+ def __draw(self):
+ """ Strokes the rectangles and draws the curves """
+ if (self.__context == None):
+ return
+ # stroke the outer rectangle
+ self.__context.rectangle(0, 0, self.__rect.width, self.__rect.height)
+ self.__set_context_color(self.__context, self.__colors["bg"])
+ self.__context.fill_preserve()
+ self.__context.stroke()
+
+ # stroke the inner rectangle
+ self.__context.rectangle(self.__inner.x,
+ self.__inner.y,
+ self.__inner.width,
+ self.__inner.height)
+ self.__set_context_color(self.__context, self.__colors["bg"])
+ self.__context.fill_preserve()
+ self.__context.stroke()
+
+ # stroke the quad around
+ self.__context.move_to(self.__inner.x, self.__inner.y + self.__inner.height)
+ self.__context.line_to(self.__inner.x, self.__inner.y)
+ self.__context.line_to(self.__inner.x + self.__inner.width - self.__mesh_x, self.__inner.y)
+ self.__context.line_to(self.__inner.x + self.__inner.width - self.__mesh_x, self.__inner.y + self.__inner.height)
+ self.__set_context_color(self.__context, (255, 255, 255))
+ self.__context.stroke()
+
+ # draw the actual bandwidth curves
+ self.__draw_bw(self.__in, self.__colors["fg_in"])
+ self.__draw_bw(self.__out, self.__colors["fg_out"])
+
+ # draw minimum, middle and max numbers
+ self.__draw_num(self.__inner.height + self.__inner.y, self.__str_min, (255, 255, 255))
+ self.__draw_num(self.__rect.height/2, self.__str_mid, (255, 255, 255))
+ self.__draw_num(self.__inner.y, self.__str_max, (255, 255, 255))
+
+ def __draw_num(self, ypos, num, color):
+ """
+ The leftmost column is used to draw info about maximum, minimum
+ and average bw
+ """
+ self.__context.move_to(5, ypos)
+ self.__context.show_text(num)
+ self.__set_context_color(self.__context, color)
+ self.__context.stroke()
+
+ def __draw_bw(self, bw_list, color):
+ """ Draws a curve from points stored in bw_list in color """
+ self.__context.move_to(self.__inner.x, self.__inner.y + self.__inner.height)
+ self.__set_context_color(self.__context, color)
+
+ x = self.__inner.x + self.__mesh_x
+ for i in bw_list[1:]:
+ self.__context.line_to(x, i)
+ x += self.__mesh_x
+ self.__context.stroke()
+
+ def __convert_one_hist(self, hist):
+ """
+ Maps values from one history object to real coordinates of the
+ drawing area
+ """
+ converted = list()
+
+ if self.__mesh_y == 0:
+ return [self.__inner.height + self.__inner.y] * len(hist)
+
+ for item in hist:
+ if item <= 100: item = 0 # treshold to get rid of really small peaks
+ converted.append((self.__inner.height - int(item / self.__mesh_y)) + self.__inner.y)
+ return converted
+
+ def __convert_points(self):
+ """
+ The bandwidth history object has the bandwidth stored as bytes. This method
+ converts the bytes into actual coordiantes of the rectangle displayed
+ """
+ # compute the aspect ratio
+ self.__mesh_x = float(self.__inner.width) / float(self.__size)
+ self.__mesh_y = float(self.maxval) / float(self.__inner.height)
+
+ self.__in = self.__convert_one_hist(self.__hist["in"])
+ self.__out = self.__convert_one_hist(self.__hist["out"])
+
+ def __get_max(self):
+ """ Finds the maximum value in both incoming and outgoing queue """
+ if self.__hist["in"]:
+ maxin = max(self.__hist["in"])
+ else:
+ maxin = 0
+ if self.__hist["out"]:
+ maxout = max(self.__hist["out"])
+ else:
+ maxout = 0
+ self.maxval = max(maxin, maxout)
+
+ def __text_size(self):
+ """ Computes the size of the text and thus the left border """
+ val = self.maxval
+ if val == 0 and self.maxval != 0:
+ val = self.maxval
+
+ self.__str_max = "%d %s" % (val, _("Bytes"))
+ self.__str_mid = "%d %s" % (val/2, _("Bytes"))
+ LoadGraph.padding["left"] = self.__context.text_extents(self.__str_max)[2] + 10
+
+ def __on_size(self, rect):
+ """ rect => a rectangle holding the size of the widget """
+ self.__rect = rect
+
+ self.__inner.x = LoadGraph.padding["left"]
+ self.__inner.y = LoadGraph.padding["top"]
+ self.__inner.width = rect.width - LoadGraph.padding["right"] - self.__inner.x
+ self.__inner.height = rect.height - LoadGraph.padding["bottom"] - self.__inner.y
+
+ self.__convert_points()
+
+ def on_expose(self, widget, event):
+ """ A signal handler that is called every time we need to redraw
+ the widget """
+ self.__context = widget.window.cairo_create()
+
+ self.__get_max()
+ self.__text_size()
+ self.__on_size(widget.get_allocation())
+
+ self.__draw()
+
+ return False
+
+ def set_history(self, hist):
+ """ Called typically on change of interface displayed """
+ self.__hist = hist
+ self.__convert_points()
+
+ def update(self):
+ """ Redraws the area """
+ alloc = self.__widget.get_allocation()
+ self.__widget.queue_draw_area(0, 0, alloc.width, alloc.height)
+
+ def change_colors(self, col_in = None, col_out = None):
+ """ Sets the colors to draw the curves with """
+ if ( col_in ) : self.__colors["fg_in"] = col_in
+ if ( col_out ) : self.__colors["fg_out"] = col_out
+
+ def set_color(self, *args, **kwargs):
+ """ Sets the colors of the graph """
+ for key, value in kwargs.items():
+ self.__colors[key] = value
+
+class Monitor:
+ def __init__(self):
+ self.window = gtk.Window()
+ self.window.set_title(_("Network monitor"))
+ self.window.set_default_size(640, 440)
+ self.window.connect('delete-event', lambda *w: gtk.main_quit())
+
+ self.main_vbox = gtk.VBox()
+ self.window.add(self.main_vbox)
+
+ # notebook
+ self.notebook = gtk.Notebook()
+ self.main_vbox.pack_start(self.notebook)
+ #self.notebook.connect('switch-page', self.show_net_status)
+
+ self.ifaces = readnet()
+ self.enabled_ifaces = []
+
+ sorted_ifaces = self.ifaces.keys()
+ sorted_ifaces.sort()
+
+ net=readnet()
+ for iface in sorted_ifaces:
+ data_in, data_out = get_traffic(iface,net)
+ self.ifaces[iface] = {'data_in': 0,
+ 'data_out': 0,
+ 'total_in': 0,
+ 'total_out': 0,
+ 'widget_in': None,
+ 'widget_out': None,
+ 'widget_speed_in': None,
+ 'widget_speed_out': None,
+ 'widget_histo_in': None,
+ 'widget_histo_out': None,
+ 'graph': None,
+ 'histogram': [],
+ 'address': "",
+ }
+ iface_stat = self.build_iface_stat(iface)
+ self.notebook.append_page(iface_stat, gtk.Label(iface))
+ if self.check_network_accounting(iface):
+ self.enabled_ifaces.append(iface)
+
+ # configure timer
+ gobject.timeout_add(1000, self.update)
+
+ self.window.show_all()
+
+ def update(self, interval=1):
+ """Updates traffic counters (interval is in seconds)"""
+ net=readnet()
+ readwireless()
+ for iface in self.ifaces:
+ status = get_status(iface)
+ old_data_in = self.ifaces[iface]['data_in']
+ old_data_out = self.ifaces[iface]['data_out']
+ total_in = self.ifaces[iface]['total_in']
+ total_out = self.ifaces[iface]['total_out']
+ data_in, data_out = get_traffic(iface, net)
+ # is it the first measure?
+ if old_data_in == 0 and old_data_out == 0:
+ old_data_in = data_in
+ old_data_out = data_out
+ # check total download
+ diff_in = data_in - old_data_in
+ diff_out = data_out - old_data_out
+ # checking for 32bits overflow
+ if diff_in < 0:
+ diff_in += 2**32
+ if diff_out < 0:
+ diff_out += 2**32
+ total_in += diff_in
+ total_out += diff_out
+ # speed
+ speed_in = diff_in / interval
+ speed_out = diff_out / interval
+ # calculating histogram
+ histogram_in = self.ifaces[iface]['histogram']['in']
+ histogram_out = self.ifaces[iface]['histogram']['out']
+ if histogram_in:
+ histo_in = reduce(lambda x, y: x+y, histogram_in) / HISTOGRAM_SIZE
+ else:
+ histo_in = 0
+ if histogram_out:
+ histo_out = reduce(lambda x, y: x+y, histogram_out) / HISTOGRAM_SIZE
+ else:
+ histo_out = 0
+ # update saved values
+ self.ifaces[iface]['data_in'] = data_in
+ self.ifaces[iface]['data_out'] = data_out
+ self.ifaces[iface]['total_in'] = total_in
+ self.ifaces[iface]['total_out'] = total_out
+ # update widgets
+ ip, mac = get_address(iface)
+ for widget, value in [('widget_in', total_in),
+ ('widget_out', total_out),
+ ('widget_speed_in', speed_in),
+ ('widget_speed_out', speed_out),
+ ('widget_histo_in', histo_in),
+ ('widget_histo_out', histo_out),
+ ('widget_ip_address', ip),
+ ('widget_status', status),
+ ('widget_hw_address', mac),
+ ]:
+ if widget in self.ifaces[iface]:
+ self.ifaces[iface][widget].set_text(str(value))
+ else:
+ print "%s not found in %s" % (widget, iface)
+ # updating graph
+ hist_in = self.ifaces[iface]['histogram']['in']
+ hist_in.append(speed_in)
+ if len(hist_in) > HISTOGRAM_SIZE:
+ del hist_in[0]
+ hist_out = self.ifaces[iface]['histogram']['out']
+ hist_out.append(speed_out)
+ if len(hist_out) > HISTOGRAM_SIZE:
+ del hist_out[0]
+ graph = self.ifaces[iface]['graph']
+ graph.update()
+ gobject.timeout_add(interval * 1000, self.update)
+
+ def check_network_accounting(self, iface):
+ """Checks if network accounting was enabled on interface"""
+ try:
+ os.stat("/var/lib/vnstat/%s" % iface)
+ return True
+ except:
+ return False
+
+ def show_statistics_dialog(self, widget, iface):
+ """Shows statistics dialog"""
+ dialog = gtk.Dialog(_("Network statistics for %s") % iface,
+ self.window, 0,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK)
+ )
+ # statistics vbox
+ stats_vbox = dialog.vbox
+ if self.check_network_accounting(iface):
+ # graph
+ graph_vnstat = gtk.Image()
+ pixbuf = self.load_graph_from_vnstat(iface, type="summary")
+ graph_vnstat.set_from_pixbuf(pixbuf)
+ stats_vbox.pack_start(graph_vnstat)
+ # buttons
+ frame = gtk.Frame(_("Network traffic statistics for %s") % iface)
+ stats_vbox.add(frame)
+ vbox = gtk.VBox()
+ frame.add(vbox)
+ # summary
+ button = gtk.RadioButton(None, _("Summary"))
+ button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "summary"))
+ vbox.pack_start(button, False, False)
+ # summary
+ button = gtk.RadioButton(button, _("Hourly traffic"))
+ button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "hourly"))
+ vbox.pack_start(button, False, False)
+ # summary
+ button = gtk.RadioButton(button, _("Daily traffic"))
+ button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "daily"))
+ vbox.pack_start(button, False, False)
+ # summary
+ button = gtk.RadioButton(button, _("Monthly traffic"))
+ button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "monthly"))
+ vbox.pack_start(button, False, False)
+ # summary
+ button = gtk.RadioButton(button, _("Top 10 traffic days"))
+ button.connect('toggled', self.update_stat_iface, (iface, graph_vnstat, "top"))
+ vbox.pack_start(button, False, False)
+ else:
+ label = gtk.Label(_("Network accounting was not enabled on interface %s.\nPlease enable network accounting on the interface in order to view traffic statistics."))
+ stats_vbox.add(label)
+
+ stats_vbox.show_all()
+ ret = dialog.run()
+ dialog.destroy()
+
+ def build_iface_stat(self, iface):
+ """Builds graphical view for interface"""
+ traf_vbox = gtk.VBox()
+ # graph
+ draw = gtk.DrawingArea()
+ traf_vbox.pack_start(draw)
+ histogram = {"in": [], "out": []}
+ graph = LoadGraph(draw, histogram, HISTOGRAM_SIZE)
+ draw.connect('expose_event', graph.on_expose)
+ self.ifaces[iface]['graph'] = graph
+ self.ifaces[iface]['histogram'] = histogram
+
+ frame_global = gtk.Frame(_("Interface settings"))
+ traf_vbox.pack_start(frame_global, False, False)
+ vbox_global = gtk.VBox(spacing=5)
+ frame_global.add(vbox_global)
+ # configuring callbacks
+ sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ # interface
+ iface_h, iface_p = self.build_value_pair(sizegroup, _("Network interface:"), iface)
+ vbox_global.pack_start(iface_h, False, False)
+ iface_s, iface_status = self.build_value_pair(sizegroup, _("Device status:"))
+ self.ifaces[iface]["widget_status"] = iface_status
+ vbox_global.pack_start(iface_s, False, False)
+ iface_addr_s, iface_addr = self.build_value_pair(sizegroup, _("IP Address:"))
+ self.ifaces[iface]["widget_ip_address"] = iface_addr
+ vbox_global.pack_start(iface_addr_s, False, False)
+ iface_mac_s, iface_mac = self.build_value_pair(sizegroup, _("Hardware address:"))
+ self.ifaces[iface]["widget_hw_address"] = iface_mac
+ vbox_global.pack_start(iface_mac_s, False, False)
+
+ # traffic
+ frame = gtk.Frame(_("Traffic statistics"))
+ traf_vbox.pack_start(frame)
+ vbox = gtk.VBox(spacing=5)
+ frame.add(vbox)
+ total_in_h, total_in = self.build_value_pair(sizegroup, _("Received data:"))
+ self.ifaces[iface]["widget_in"] = total_in
+ vbox.pack_start(total_in_h, False, False)
+ total_out_h, total_out = self.build_value_pair(sizegroup, _("Sent data:"))
+ self.ifaces[iface]["widget_out"] = total_out
+ vbox.pack_start(total_out_h, False, False)
+ speed_in_h, speed_in = self.build_value_pair(sizegroup, _("Download speed:"))
+ self.ifaces[iface]["widget_speed_in"] = speed_in
+ vbox.pack_start(speed_in_h, False, False)
+ speed_out_h, speed_out = self.build_value_pair(sizegroup, _("Upload speed:"))
+ self.ifaces[iface]["widget_speed_out"] = speed_out
+ vbox.pack_start(speed_out_h, False, False)
+ histo_in_h, histo_in = self.build_value_pair(sizegroup, _("Average download speed (over past %d samples):") % HISTOGRAM_SIZE)
+ self.ifaces[iface]["widget_histo_in"] = histo_in
+ vbox.pack_start(histo_in_h, False, False)
+ histo_out_h, histo_out = self.build_value_pair(sizegroup, _("Average upload speed (over past %d samples):") % HISTOGRAM_SIZE)
+ self.ifaces[iface]["widget_histo_out"] = histo_out
+ vbox.pack_start(histo_out_h, False, False)
+
+ # statistics button
+ if self.check_network_accounting(iface):
+ button = gtk.Button(_("Show detailed network statistics"))
+ button.connect('clicked', self.show_statistics_dialog, iface)
+ traf_vbox.pack_start(button, False, False)
+ else:
+ label = gtk.Label("\n".join(textwrap.wrap(_("Network accounting is not enabled for this interface. Please enable it in Mandriva network center in order to view detailed traffic statistics"))))
+ traf_vbox.pack_start(label, False, False)
+
+ return traf_vbox
+
+ def build_value_pair(self, sizegroup, text, value_text=None):
+ """Builds a value pair"""
+ hbox = gtk.HBox(spacing=10)
+ name = gtk.Label(text)
+ name.set_property("xalign", 1.0)
+ hbox.pack_start(name, False, False)
+ value = gtk.Label(value_text)
+ value.set_property("xalign", 0.0)
+ hbox.pack_start(value, False, False)
+ sizegroup.add_widget(name)
+ return hbox, value
+
+ def update_stat_iface(self, widget, data):
+ """Updates graphic statistics"""
+ iface, graph, type = data
+ pixbuf = self.load_graph_from_vnstat(iface, type)
+ graph.set_from_pixbuf(pixbuf)
+
+ def load_graph_from_vnstat(self, iface, type="hourly"):
+ """Loads graph from vnstat. Right now uses vnstati to do all the dirty job"""
+ # load image from data
+ if type == "hourly":
+ param="-h"
+ elif type == "monthly":
+ param="-m"
+ elif type == "daily":
+ param="-d"
+ elif type == "top":
+ param="-t"
+ elif type == "summary":
+ param="-s"
+ else:
+ # show summary if parameter is unknown
+ print "Unknown parameter %s, showing summary.." % type
+ param="-s"
+ data = os.popen("vnstati %s -o - -i %s" % (param, iface)).read()
+ loader = gtk.gdk.PixbufLoader()
+ loader.write(data)
+ loader.close()
+ pixbuf = loader.get_pixbuf()
+ return pixbuf
+
+if __name__ == "__main__":
+ monitor = Monitor()
+ gtk.main()
diff --git a/src/wifi.py b/src/wifi.py
new file mode 100644
index 0000000..28e06a6
--- /dev/null
+++ b/src/wifi.py
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+"""net_monitor: wifi monitoring"""
+
+# native librari implements a few bits
+import _native
+import socket
+import fcntl
+import struct
+import traceback
+import array
+
+
+class Wireless:
+ # based on http://svn.pardus.org.tr/pardus/tags/pardus-1.0/system/base/wireless-tools/comar/link.py
+
+ # wireless IOCTL constants
+ SIOCGIWMODE = 0x8B07 # get operation mode
+ SIOCGIWRATE = 0x8B21 # get default bit rate
+ SIOCGIWESSID = 0x8B1B # get essid
+
+ # wireless modes
+ modes = ['Auto', 'Ad-Hoc', 'Managed', 'Master', 'Repeat', 'Second', 'Monitor']
+
+ def __init__(self):
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+ def ioctl(self, func, params):
+ return fcntl.ioctl(self.sock.fileno(), func, params)
+
+ def get_max_quality(self, iface):
+ """Gets maximum quality value"""
+ return _native.wifi_get_max_quality(iface)
+
+ def call(self, iface, func, arg=None):
+ if not arg:
+ data = (iface + '\0' * 32)[:32]
+ else:
+ data = (iface + '\0' * 16)[:16] + arg
+ try:
+ res = self.ioctl(func, data)
+ return res
+ except:
+ traceback.print_exc()
+ return None
+
+ def get_essid(self, iface):
+ """Get current essid for an interface"""
+ buffer = array.array('c', '\0' * 16)
+ addr, length = buffer.buffer_info()
+ arg = struct.pack('Pi', addr, length)
+ self.call(iface, self.SIOCGIWESSID, arg)
+ return buffer.tostring().strip('\0')
+
+ def get_mode(self, iface):
+ """Get current mode from an interface"""
+ result = self.call(iface, self.SIOCGIWMODE)
+ mode = struct.unpack("i", result[16:20])[0]
+ return self.modes[mode]
+
+ def get_bitrate(self, iface):
+ """Gets current operating rate from an interface"""
+ # Note: KILO is not 2^10 in wireless tools world
+
+ result = self.call(iface, self.SIOCGIWRATE)
+
+ if result:
+ size = struct.calcsize('ihbb')
+ m, e, i, pad = struct.unpack('ihbb', result[16:16+size])
+ if e == 0:
+ bitrate = m
+ else:
+ bitrate = float(m) * 10**e
+ return bitrate
+ else:
+ return -1