diff options
author | Eugeni Dodonov <eugeni@mandriva.com> | 2009-09-29 14:18:24 -0300 |
---|---|---|
committer | Eugeni Dodonov <eugeni@mandriva.com> | 2009-09-29 14:18:24 -0300 |
commit | e6aa6bf53cb954f1a937f6cafc79e4c60c172cd3 (patch) | |
tree | d3bc3b61d6e44a39a7c24ab4b970810e6ecb9e66 /src | |
parent | 9795b7728134d4d939c0f364f37ac1a378de434c (diff) | |
download | net_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__.py | 0 | ||||
-rw-r--r-- | src/_native.c | 229 | ||||
-rwxr-xr-x | src/net_monitor | 595 | ||||
-rw-r--r-- | src/wifi.py | 75 |
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 |