/* GKRELLMWireless | Copyright (C) 2000-2002 Sjoerd Simons | | Author: Sjoerd Simons sjoerd@gkrellm.luon.net | Latest versions might be found at: http://gkrellm.luon.net | | This program is free software which I release under the GNU General Public | License. You may redistribute and/or modify this program under the terms | of that 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. | | To get a copy of the GNU General Puplic License, write to the | Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "wireless.h" static GkrellmMonitor *monitor; static gint panel_style_id; static wcard_t *cards = NULL; static GtkWidget *PanelBox; static void reset_panel(int create); wcard_t * new_wcard(gchar *interface,int found, int flags) { /* creates an new wcard entry for interface, and sets the flags if it's not a newly found card */ wcard_t *newcard; wcard_t *card; newcard = malloc(sizeof(wcard_t)); newcard->next = NULL; newcard->ifname = strdup(interface); newcard->level_panel = NULL; newcard->link_panel = NULL; newcard->noise_panel = NULL; if (found) newcard->flags = (0 | SHOW | SHOW_LINK | SHOW_LEVEL | SHOW_NOISE); else /* a card wich is not detected, but from a config file */ newcard->flags = flags & ~ACTIVE; if (cards == NULL) cards = newcard; else { for (card = cards ; card->next != NULL; card = card->next); card->next = newcard; } return newcard; } void del_wcard(wcard_t *card) { /* called when a card isn't active anymore, sets it's entry to */ /* uninitialized */ card->flags &= (~ACTIVE); reset_panel(0); } wcard_t *found_wcard(gchar *interface) { /* called by the system specific find_wcard functions */ /* for each wireless card it had found */ /* adds/modifies an wireless card entry for it */ /* returns the entry, so it's system specific entry can be edited */ wcard_t *card; for (card = cards ; card != NULL ; card = card->next) { if (strcmp(card->ifname,interface)) { /* wrong entry */ continue; } if (card->flags & ACTIVE) { /* already showing this one */ return NULL; } else { card->flags |= ACTIVE; return card; } /* found the entry */ } /* never saw this card before, creating new entry */ card = new_wcard(interface,1,0); card->flags |= ACTIVE; gkrellm_config_modified(); return card; } /* system specific stuff */ #if defined(__FreeBSD__) || defined(__NetBSD__) /* FreeBSD & NetBSD specific */ #if defined(__FreeBSD__) && __FreeBSD_version >= 600034 static gint find_scan_card(void) { gint ret = FALSE; struct ifaddrs *res = NULL, *ifa = NULL; struct ifmediareq ifmr; int s; if (getifaddrs(&res) != 0) return ret; if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { freeifaddrs(res); return ret; } for (ifa = res; ifa; ifa = ifa->ifa_next) { memset(&ifmr, 0, sizeof(ifmr)); strncpy(ifmr.ifm_name, ifa->ifa_name, sizeof(ifmr.ifm_name)); if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) continue; if (!(ifmr.ifm_status & IFM_AVALID)) continue; if (IFM_TYPE(ifmr.ifm_active) != IFM_IEEE80211) continue; if (found_wcard(ifa->ifa_name) != NULL) ret = TRUE; } close(s); freeifaddrs(res); return ret; } #else static int find_wi_card(void) { /* possible interfaces */ char interfaces[][4] = {"wi0","wi1","wi2"}; /* wireless info request struct */ struct wi_req wreq; /* interface request struct */ struct ifreq ifr; int s,i,ret; ret = FALSE; /* open a socket for ioctl's */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return ret; /* clean out the wreq struct */ memset(&wreq, 0, sizeof(wreq)); wreq.wi_len = WI_MAX_DATALEN; /* we want to know the quality */ wreq.wi_type = WI_RID_COMMS_QUALITY; for (i = 0 ; i < 3 ; i++ ) { memset(&ifr,0, sizeof(ifr)); /* ask information about a certain interface */ strcpy(ifr.ifr_name,interfaces[i]); ifr.ifr_data = (caddr_t)&wreq; /* get information about this interface, if it excists it's an card */ if (ioctl(s, SIOCGWAVELAN, &ifr) == -1) continue; if(found_wcard(interfaces[i]) != NULL) ret = TRUE; } close(s); return ret; } #if !defined(__NetBSD__) static gint find_an_card(void) { char interfaces[][4] = {"an0","an1","an2"}; int i,s,ret ; struct ifreq ifr; struct an_req areq; ret = FALSE; /* open a socket for ioctl's */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return ret; for (i = 0 ; i < 3 ; i++ ) { memset(&ifr,0, sizeof(ifr)); /* ask information about a certain interface */ strcpy(ifr.ifr_name,interfaces[i]); areq.an_len = AN_MAX_DATALEN; areq.an_type = AN_RID_READ_CACHE; ifr.ifr_data = (caddr_t)&areq; if (ioctl(s,SIOCGAIRONET,&ifr) == -1) continue; if(found_wcard(interfaces[i]) != NULL) ret = TRUE; } close(s); return ret; } #endif /* !defined(__NetBSD__) */ #endif /* defined(__FreeBSD__) && __FreeBSD_version >= 600034 */ static gint find_wlancard(void) { gint ret = FALSE; #if defined(__FreeBSD__) && __FreeBSD_version >= 600034 ret = find_scan_card(); #else ret = find_wi_card(); #if !defined(__NetBSD__) ret = find_an_card() || ret; #endif /* !defined(__NetBSD__) */ #endif /* defined(__FreeBSD__) && __FreeBSD_version >= 600034 */ return ret; } #if defined(__FreeBSD__) && __FreeBSD_version >= 600034 static int get_scan_link_quality(wcard_t *card, float *quality, float *level, float *noise) { int s; uint8_t buf[24 * 1024]; struct ieee80211req ireq; struct ieee80211req_scan_result *sr; /* open a socket for ioctl's */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return FALSE; memset(&ireq, 0, sizeof(ireq)); strncpy(ireq.i_name, card->ifname, sizeof(ireq.i_name)); ireq.i_type = IEEE80211_IOC_SCAN_RESULTS; ireq.i_data = buf; ireq.i_len = sizeof(buf); if (ioctl(s, SIOCG80211, &ireq) < 0) { close(s); return FALSE; } close(s); if (ireq.i_len < sizeof(struct ieee80211req_scan_result)) return FALSE; sr = (struct ieee80211req_scan_result *) buf; *quality = sr->isr_intval; *level = sr->isr_rssi; *noise = sr->isr_noise; return TRUE; } #endif /* defined(__FreeBSD__) && __FreeBSD_version >= 600034 */ #if !defined(__FreeBSD__) || __FreeBSD_version < 700052 static int get_wi_link_quality(wcard_t *card, float *quality, float *level, float *noise) { /* wireless info request struct */ struct wi_req wreq; /* interface request struct */ struct ifreq ifr; int s; /* open a socket for ioctl's */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return FALSE; /* clean out the wreq struct */ memset(&wreq, 0, sizeof(wreq)); wreq.wi_len = WI_MAX_DATALEN; /* we want to know the quality */ wreq.wi_type = WI_RID_COMMS_QUALITY; memset(&ifr,0, sizeof(ifr)); /* ask information about a certain interface */ strcpy(ifr.ifr_name,card->ifname); ifr.ifr_data = (caddr_t)&wreq; /* get information about this interface */ if (ioctl(s, SIOCGWAVELAN, &ifr) == -1) { close(s); return FALSE; } close(s); *quality = wreq.wi_val[0]; *level = wreq.wi_val[1]; *noise = wreq.wi_val[2]; return TRUE; } #endif /* !defined(__FreeBSD__) || __FreeBSD_version < 700052 */ #if !defined(__NetBSD__) static int get_an_link_quality(wcard_t *card, float *quality, float *level, float *noise) { int nr,s; struct ifreq ifr; struct an_req areq; struct an_sigcache *sc; /* open a socket for ioctl's */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return FALSE; memset(&ifr,0, sizeof(ifr)); memset(&areq,0, sizeof(areq)); /* ask information about a certain interface */ strcpy(ifr.ifr_name,card->ifname); areq.an_len = AN_MAX_DATALEN; areq.an_type = AN_RID_READ_CACHE; ifr.ifr_data = (caddr_t)&areq; if (ioctl(s,SIOCGAIRONET,&ifr) == -1) { close(s); return FALSE; } close(s); /* anval excists out of a integer which represents the number of sigcaches*/ /* followed by the sigcaches */ nr = (int) *areq.an_val; /* number of signal caches */ if (nr == 0) return FALSE; /* we use the first sigcache for statistics, which seems to work ok for me */ sc = (struct an_sigcache *) ((char *) &areq.an_val + sizeof(int)); *quality = sc->quality; *level = sc->signal; *noise = sc->noise; return TRUE; } #endif /* !defined(__NetBSD__) */ static int get_link_quality(wcard_t *card, float *quality, float *level, float *noise) { #if defined(__FreeBSD__) && __FreeBSD_version >= 600034 if (strncmp(card->ifname, "an", 2) == 0 && isnumber(card->ifname[2])) return get_an_link_quality(card,quality,level,noise); #if !defined(__FreeBSD__) || __FreeBSD_version < 700052 else if (strncmp(card->ifname, "wi", 2) == 0 && isnumber(card->ifname[2])) return get_wi_link_quality(card,quality,level,noise); #endif /* !defined(__FreeBSD__) || __FreeBSD_version < 700052 */ return get_scan_link_quality(card,quality,level,noise); #else switch (card->ifname[0]) { #if !defined(__NetBSD__) case 'a': /* an card */ return get_an_link_quality(card,quality,level,noise); #endif /* !defined(__NetBSD__) */ case 'w': /* wi card */ return get_wi_link_quality(card,quality,level,noise); } return FALSE; #endif } #endif #ifdef __linux__ /* Linux Specific*/ #define WIRELESS "/proc/net/wireless" static gint find_wlancard(void) { FILE *procfile; int ret = FALSE; char iface[5],procread[256], *c; if ((procfile = fopen(WIRELESS,"r")) == NULL) return FALSE; /* 2 lines header */ fgets(procread,sizeof(procread),procfile); fgets(procread,sizeof(procread),procfile); while(fgets(procread,sizeof(procread),procfile) != NULL) { sscanf(procread,"%s: %*s %*f %*f %*f %*d %*d %*d", iface); c = strstr(iface,":"); *c = '\0'; if(found_wcard(iface) != NULL) ret = TRUE; } fclose(procfile); return ret; } float get_next_fl(char **string) { char *c; float ret; c = *string; while (!isdigit(*c) && *c != '\0') c++; ret = atof(*string); while(!isspace(*c) && *c != '\0') c++; *string = c; return ret; } static int get_link_quality(wcard_t *card, float *link, float *level, float *noise) { FILE *procfile; char procread[256], *c; if ((procfile = fopen(WIRELESS,"r")) != NULL) { /* 2 lines header */ fgets(procread,sizeof(procread),procfile); fgets(procread,sizeof(procread),procfile); /* search for our card */ while (fgets(procread,sizeof(procread),procfile) != NULL) { c = procread; while (isspace(*c)) c++; if (!strncmp(c,card->ifname,strlen(card->ifname))) { while (!isspace(*c) && *c != '\0') c++; get_next_fl(&c); /* status thingie */ *link = get_next_fl(&c); *level = get_next_fl(&c); *noise = get_next_fl(&c); fclose(procfile); return TRUE; } } fclose(procfile); } del_wcard(card); return FALSE; } #endif /* end of system specific code */ static gint panel_expose_event(GtkWidget *widget, GdkEventExpose *event, GkrellmPanel *panel) { gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)], panel->pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } static void del_panel(GkrellmPanel *panel) { if (panel == NULL) return; gkrellm_destroy_decal_list(panel); gkrellm_destroy_krell_list(panel); gkrellm_panel_destroy(panel); gkrellm_pack_side_frames(); } static void create_panel(GkrellmPanel **rpanel, GkrellmDecal **text, int fullscale,int create) { GkrellmKrell *k; GkrellmStyle *style; GkrellmPiximage *krell_image; int first_create = 0 ; GkrellmPanel *panel = *rpanel; if (panel == NULL) { panel = gkrellm_panel_new0(); first_create = 1; } else { gkrellm_destroy_decal_list(panel); gkrellm_destroy_krell_list(panel); } style = gkrellm_meter_style(panel_style_id); krell_image = gkrellm_krell_meter_piximage(panel_style_id); k = gkrellm_create_krell(panel, krell_image, style); gkrellm_set_krell_full_scale(k, fullscale, 1); panel->textstyle = gkrellm_meter_textstyle(panel_style_id); *text = gkrellm_create_decal_text(panel,"0" ,panel->textstyle,style,-1,-1,-1); gkrellm_panel_configure(panel,NULL,style); gkrellm_panel_create(PanelBox, monitor, panel); if (first_create || create) { g_signal_connect(GTK_OBJECT(panel->drawing_area), "expose_event", G_CALLBACK(panel_expose_event),panel); } gkrellm_draw_decal_text(panel,*text,"wireless",-10); gkrellm_draw_panel_layers(panel); gkrellm_pack_side_frames(); *rpanel = panel; } static void reset_panel(int create) { /* (re)create all the needed panels */ /* if create then gkrellm is newly build (theme switch or whatever) */ /* else it's called by the plugin itself (new card, changed config) */ wcard_t *card; for (card = cards ; card != NULL ; card = card->next ) { if (!(card->flags & ACTIVE) || !(card->flags & SHOW)) { del_panel(card->level_panel); card->level_panel = NULL; del_panel(card->link_panel); card->link_panel = NULL; del_panel(card->noise_panel); card->noise_panel = NULL; continue; } if (card->flags & SHOW_LINK) create_panel(&(card->link_panel),&(card->link_text),LINKQ_MAX,create); else { del_panel(card->link_panel); card->link_panel = NULL; } if (card->flags & SHOW_LEVEL) create_panel(&(card->level_panel),&(card->level_text),LEVEL_MAX,create); else { del_panel(card->level_panel); card->level_panel = NULL; } if (card->flags & SHOW_NOISE) create_panel(&(card->noise_panel),&(card->noise_text),NOISE_MAX,create); else { del_panel(card->noise_panel); card->noise_panel = NULL; } } } static void update_panel(GkrellmPanel *panel, GkrellmDecal *decal , char *header , char *ifname,float amount) { GkrellmKrell *k; char text[50]; if (panel == NULL) return; snprintf(text,sizeof(text),"%s: %.0f %s", ifname, amount, header); k = KRELL(panel); k->previous = 0; gkrellm_update_krell(panel,k,amount); gkrellm_draw_decal_text(panel,decal,text,amount); gkrellm_draw_panel_layers(panel); } static void update_plugin (void) { wcard_t *card; float link = 0, level = 0, noise =0; if (GK.second_tick) { for (card = cards; card != NULL; card = card->next) { if (!(card->flags & ACTIVE) || !(card->flags & SHOW)) continue; /* get_quality_info: get info in a system dependant way */ if (!get_link_quality(card,&link,&level,&noise)) return; update_panel(card->level_panel,card->level_text ,"Level",card->ifname,level); update_panel(card->link_panel,card->link_text ,"Link",card->ifname,link); update_panel(card->noise_panel,card->noise_text ,"Noise",card->ifname,noise); } } if (GK.two_second_tick) { if (find_wlancard() == TRUE) { reset_panel(0); } } } static void create_plugin(GtkWidget *vbox, gint first_create) { if (first_create) { PanelBox = vbox; /* first time it's created, only save panelbox here */ } else { reset_panel(1); /* rebuild the panels */ } } static void save_plugin_config (FILE *f) { wcard_t *card; for (card = cards; card != NULL ; card = card->next) { fprintf(f,"%s %s %d\n",PLUGIN_CONFIG_KEYWORD,card->ifname,card->flags); } } static void load_plugin_config(gchar *arg) { gchar ifname[10]; int flags; if (sscanf(arg,"%s %d\n",ifname,&flags) == 2) { new_wcard(ifname,0,flags); } } /* the config panel stuff and callbacs */ static void button_toggled(GtkWidget *widget, wcard_t *card, int value) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) card->cflags |= value; else card->cflags &= (~value); } static void cb_show_button_toggled(GtkWidget *widget, wcard_t *card) { button_toggled(widget,card,SHOW); } static void cb_link_button_toggled(GtkWidget *widget, wcard_t *card) { button_toggled(widget,card,SHOW_LINK); } static void cb_level_button_toggled(GtkWidget *widget, wcard_t *card) { button_toggled(widget,card,SHOW_LEVEL); } static void cb_noise_button_toggled(GtkWidget *widget, wcard_t *card) { button_toggled(widget,card,SHOW_NOISE); } static void create_toggle_button(char *name,int active, wcard_t *card, GtkWidget *box, GtkSignalFunc func) { GtkWidget *button; button = gtk_check_button_new_with_label(name); gtk_box_pack_start(GTK_BOX(box),button,FALSE,TRUE,3); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),active); g_signal_connect(G_OBJECT(button),"toggled", G_CALLBACK(func),card); } static void create_device_tab(GtkWidget *notebook,wcard_t *card) { GtkWidget *label; GtkWidget *frame; GtkWidget *vbox; GtkWidget *separator; label = gtk_label_new(card->ifname); frame = gtk_frame_new(NULL); gtk_notebook_append_page(GTK_NOTEBOOK(notebook),frame,label); vbox = gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(frame),vbox); create_toggle_button("Show this Interface" ,card->flags & SHOW, card, vbox, (GtkSignalFunc) cb_show_button_toggled); separator = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(vbox),separator,FALSE,TRUE,3); create_toggle_button("Show link quality" ,card->flags & SHOW_LINK, card, vbox, (GtkSignalFunc) cb_link_button_toggled); create_toggle_button("Show signal level" ,card->flags & SHOW_LEVEL, card, vbox, (GtkSignalFunc) cb_level_button_toggled); create_toggle_button("Show noise level" ,card->flags & SHOW_NOISE, card, vbox, (GtkSignalFunc) cb_noise_button_toggled); } static void create_devices_tab(GtkWidget *notebook) { wcard_t *card; for (card = cards ; card != NULL; card = card->next) { create_device_tab(notebook, card); card->cflags = card->flags; } } static void create_help_text(GtkWidget *text) { gchar *info_text[] = { "This plugin allows you to monitor the quality of a wireless lan card \n", "\nConfiguration:\n", "Every detected wireless interface will have ", "one config tab with the following options:\n", "\tShow this interface: ", "\n\tShow information about the interface\n", "\tShow link quality:\n", "\tShow the link quality of this interface\n", "\tShow signal level:\n", "\tShow the signal level of this interface\n", "\tShow noise level:\n", "\tShow the noise level of this interface\n" }; gkrellm_gtk_text_view_append_strings(text,info_text, sizeof(info_text)/sizeof(gchar *)); } static void create_info_tab(GtkWidget *notebook) { GtkWidget *frame; GtkWidget *scrolled; GtkWidget *text; GtkWidget *page; frame = gtk_frame_new(NULL); gtk_container_border_width(GTK_CONTAINER(frame),3); scrolled = gtk_scrolled_window_new(NULL,NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(frame),scrolled); page = gkrellm_gtk_notebook_page(notebook,"Info"); text = gkrellm_gtk_scrolled_text_view(page,NULL, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); create_help_text(text); } static void create_about_tab(GtkWidget *notebook) { gchar *plugin_about_text; GtkWidget *text; GtkWidget *label; plugin_about_text = g_strdup_printf( "GkrellMWireless %d.%d%s\n" \ "GKrellM Wireless Plugin\n\n" \ "Copyright (C) 2000-2001 Sjoerd Simons\n" \ "sjoerd@luon.net\n" \ "http://gkrellm.luon.net \n\n" \ "Released under the GNU Public Licence", WIRELESS_MAJOR_VERSION,WIRELESS_MINOR_VERSION,WIRELESS_EXTRA_VERSION); text = gtk_label_new(plugin_about_text); label = gtk_label_new("About"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook),text,label); g_free(plugin_about_text); } static void create_plugin_config(GtkWidget *tab) { GtkWidget *notebook; notebook = gtk_notebook_new(); gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook),GTK_POS_TOP); gtk_box_pack_start(GTK_BOX(tab),notebook,TRUE,TRUE,0); create_devices_tab(notebook); create_info_tab(notebook); create_about_tab(notebook); } static void apply_plugin_config(void) { wcard_t *card; for (card = cards ; card != NULL ; card = card->next) { card->flags = card->cflags; } reset_panel(0); } /* end of config panel stuff */ static GkrellmMonitor wireless_meter = { "Wireless", /* Name, for config tab. */ 0, /* Id, 0 if a plugin */ create_plugin, /* The create function */ update_plugin, /* The update function */ create_plugin_config, /* The config tab create function */ apply_plugin_config, /* Apply the config function */ save_plugin_config, /* Save user config */ load_plugin_config, /* Load user config */ PLUGIN_CONFIG_KEYWORD, /* config keyword */ NULL, /* Undefined 2 */ NULL, /* Undefined 1 */ NULL, /* Undefined 0 */ MON_FS, /* Insert plugin before this monitor. Choose */ /* MON_CLOCK, MON_CPU, MON_PROC, MON_DISK, */ /* MON_INET, MON_NET, MON_FS, MON_MAIL, */ /* MON_APM, or MON_UPTIME */ /* Choose wisely, others have to live with it. */ NULL, /* Handle if a plugin, filled in by GKrellM */ NULL /* path if a plugin, filled in by GKrellM */ }; /* All GKrellM plugins must have one global routine named init_plugin() | which returns a pointer to a filled in monitor structure. */ GkrellmMonitor * gkrellm_init_plugin(void) { panel_style_id = gkrellm_add_meter_style(&wireless_meter,"wireless"); monitor = &wireless_meter; return monitor; }