Enabling multithreaded compilation.
[debian/lxpanel.git] / src / plugins / netstat / netstat.c
1 /**
2 * Copyright (c) 2008 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #if HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <sys/types.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <pthread.h>
29 #include <iwlib.h>
30 #include "nsconfig.h"
31 #include "netstat.h"
32 #include "lxnm_client.h"
33 #include "statusicon.h"
34 #include "passwd_gui.h"
35 #include "devproc.h"
36 #include "wireless.h"
37 #include "panel.h"
38 #include "misc.h"
39 #include "plugin.h"
40 #include "dbg.h"
41
42 /* 1 second */
43 #define NETSTAT_IFACE_POLL_DELAY 3000
44
45 static void* actionProcess(void *arg)
46 {
47 ENTER;
48 RET(GINT_TO_POINTER(system((char *)arg)));
49 }
50
51 /* menu handlers */
52 static void ethernet_repair(GtkWidget *widget, netdev_info *ni)
53 {
54 if (ni->ns->fixcmd) {
55 pthread_t actionThread;
56 pthread_attr_t attr;
57 char *fixcmd;
58
59 fixcmd = g_strdup_printf(ni->ns->fixcmd, ni->netdev_list->info.ifname);
60
61 /* Note: Better would be to join the thread, and thus get a status
62 * message. But detaching is currently simpler to implement. */
63 pthread_attr_init(&attr);
64 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
65 pthread_create(&actionThread, &attr, actionProcess, fixcmd);
66 pthread_attr_destroy(&attr);
67 } else {
68 lxnm_send_command(ni->ns->fnetd->lxnmchannel, LXNM_ETHERNET_REPAIR, ni->netdev_list->info.ifname);
69 }
70 }
71
72 static void ethernet_down(GtkWidget *widget, netdev_info *ni)
73 {
74 lxnm_send_command(ni->ns->fnetd->lxnmchannel, LXNM_ETHERNET_DOWN, ni->netdev_list->info.ifname);
75 }
76
77 static void wireless_connect(GtkWidget *widget, ap_setting *aps)
78 {
79 char *cmdargs;
80
81 /* without encryption */
82 if (!aps->apinfo->haskey) {
83 cmdargs = lxnm_wireless_command_make(aps->ifname, aps->apinfo->essid, aps->apinfo->apaddr, "",
84 aps->apinfo->en_method, aps->apinfo->key_mgmt,
85 aps->apinfo->group, aps->apinfo->pairwise);
86 lxnm_send_command(aps->gio, LXNM_WIRELESS_CONNECT, cmdargs);
87 g_free(cmdargs);
88 } else {
89 /* with encryption */
90 if (aps->ni->netdev_list->info.pg!=NULL)
91 passwd_gui_destroy(aps->ni->netdev_list->info.pg);
92
93 /* record information for password dialog */
94 ap_setting *aps_new;
95 ap_info *apinfo;
96 apinfo = malloc(sizeof(ap_info));
97 if (aps->apinfo->essid==NULL)
98 apinfo->essid = NULL;
99 else
100 apinfo->essid = g_strdup(aps->apinfo->essid);
101
102 apinfo->apaddr = g_strdup(aps->apinfo->apaddr);
103 apinfo->quality = aps->apinfo->quality;
104 apinfo->en_method = aps->apinfo->en_method;
105 apinfo->pairwise = aps->apinfo->pairwise;
106 apinfo->group = aps->apinfo->group;
107 apinfo->key_mgmt = aps->apinfo->key_mgmt;
108 apinfo->haskey = aps->apinfo->haskey;
109
110 aps_new = g_new0(ap_setting, 1);
111 aps_new->ni = aps->ni;
112 aps_new->gio = aps->gio;
113 aps_new->ifname = g_strdup(aps->ifname);
114 aps_new->apinfo = apinfo;
115 /* create dialog window for typing password */
116 aps->ni->netdev_list->info.pg = passwd_gui_new(aps_new);
117 //passwd_gui_set_style(aps->ni->netdev_list->info.pg, gtk_style_new());
118 }
119 }
120
121 static void
122 g_free_weaknotify(void *ptr, GObject *dummy)
123 {
124 g_free(ptr);
125 }
126
127 static GtkWidget *
128 wireless_menu(netdev_info *ni)
129 {
130 APLIST *aplist;
131 APLIST *ptr;
132 GtkWidget *menu;
133 GtkWidget *menu_item;
134 GtkWidget *wireless_label;
135
136 /* AP status widget */
137 GtkWidget *item_box;
138 GtkWidget *essid_label;
139 GtkWidget *lockicon;
140 GtkWidget *signal_quality;
141 gdouble quality_per;
142 ap_setting *aps;
143
144 /* create menu */
145 menu = gtk_menu_new();
146 g_signal_connect(menu, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL);
147
148 /* Scanning AP */
149 aplist = wireless_scanning(ni->ns->fnetd->iwsockfd, ni->netdev_list->info.ifname);
150 if (aplist!=NULL) {
151 /* release AP list after menu */
152 g_object_weak_ref(G_OBJECT(menu), wireless_aplist_free, aplist);
153 ptr = aplist;
154 do {
155 /* skip hidden AP with Encryption */
156 if (ptr->info->haskey&&ptr->info->essid==NULL) {
157 /* handle next AP */
158 ptr = ptr->next;
159 continue;
160 }
161
162 aps = g_new0(ap_setting, 1);
163 aps->ni = ni;
164 aps->gio = ni->ns->fnetd->lxnmchannel;
165 aps->ifname = ni->netdev_list->info.ifname;
166 aps->apinfo = ptr->info;
167
168 /* create a new item */
169 menu_item = gtk_menu_item_new();
170 item_box = gtk_hbox_new(FALSE, 0);
171
172 /* Encryption */
173 if (aps->apinfo->haskey) {
174 lockicon = gtk_image_new_from_file(ICONS_WL_LOCK);
175 gtk_box_pack_start(GTK_BOX(item_box), lockicon, FALSE, FALSE, 0);
176 }
177
178 /* ESSID */
179 if (aps->apinfo->essid==NULL)
180 essid_label = gtk_label_new(_("<Hidden Access Point>"));
181 else
182 essid_label = gtk_label_new(aps->apinfo->essid);
183
184 gtk_label_set_justify(GTK_LABEL(essid_label), GTK_JUSTIFY_LEFT);
185 gtk_misc_set_padding(GTK_MISC(essid_label), 2, 0);
186 gtk_box_pack_start(GTK_BOX(item_box), essid_label, TRUE, FALSE, 0);
187
188 /* Quality */
189 quality_per = (gdouble)((double)aps->apinfo->quality/100);
190 if (quality_per>1)
191 quality_per = 1;
192 else if (quality_per<0)
193 quality_per = 0;
194
195 signal_quality = gtk_progress_bar_new();
196 gtk_widget_set_size_request(signal_quality, 100, -1);
197 gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(signal_quality), GTK_PROGRESS_LEFT_TO_RIGHT);
198 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(signal_quality), quality_per);
199 gtk_box_pack_start(GTK_BOX(item_box), signal_quality, FALSE, FALSE, 0);
200
201 /* add this item to menu */
202 gtk_container_add(GTK_CONTAINER(menu_item), item_box);
203 gtk_menu_append(GTK_MENU(menu), menu_item);
204 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(wireless_connect), aps);
205 g_object_weak_ref(G_OBJECT(menu_item), g_free_weaknotify, aps);
206
207 /* handle next AP */
208 ptr = ptr->next;
209 } while(ptr!=NULL);
210 } else {
211 /* we do not found any wireless networks */
212 menu_item = gtk_menu_item_new();
213 wireless_label = gtk_label_new(_("Wireless Networks not found in range"));
214 gtk_label_set_justify(GTK_LABEL(wireless_label), GTK_JUSTIFY_LEFT);
215 gtk_widget_set_sensitive(GTK_WIDGET(wireless_label), FALSE);
216 gtk_container_add(GTK_CONTAINER(menu_item), wireless_label);
217 gtk_menu_append(GTK_MENU(menu), menu_item);
218 }
219
220 gtk_widget_show_all(menu);
221
222 return menu;
223 }
224
225 static gint menupopup(GtkWidget *widget, GdkEvent *event, netdev_info *ni)
226 {
227 GdkEventButton *event_button;
228
229 g_return_val_if_fail (event != NULL, FALSE);
230
231 if (event->type == GDK_BUTTON_PRESS) {
232 event_button = (GdkEventButton *) event;
233 if (event_button->button == 1) {
234 /* wireless device */
235 if (ni->netdev_list->info.wireless) {
236 gtk_menu_popup(GTK_MENU(wireless_menu(ni)), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
237 }
238 return TRUE;
239 } else if (event_button->button == 3) {
240 GtkWidget *menu;
241 GtkWidget *menu_item;
242
243 /* create menu */
244 menu = gtk_menu_new();
245
246 /* Repair */
247 menu_item = gtk_menu_item_new_with_label(_("Repair"));
248 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
249 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(ethernet_repair), ni);
250
251 /* interface down */
252 menu_item = gtk_menu_item_new_with_label(_("Disable"));
253 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
254 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(ethernet_down), ni);
255
256 gtk_widget_show_all(menu);
257
258 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
259 return TRUE;
260 }
261 }
262
263 return FALSE;
264 }
265
266 static char *select_icon(gboolean plug, gboolean connected, int stat)
267 {
268 if (!plug)
269 return ICONS_DISCONNECT;
270
271 switch(stat) {
272 case NETDEV_STAT_NORMAL:
273 return ICONS_CONNECTED;
274 break;
275 case NETDEV_STAT_PROBLEM:
276 return ICONS_PROBLEM;
277 break;
278 case NETDEV_STAT_RENEW:
279 return ICONS_RENEW;
280 break;
281 case NETDEV_STAT_BOTHRS:
282 return ICONS_BOTHRS;
283 break;
284 case NETDEV_STAT_SENDDATA:
285 return ICONS_SENDDATA;
286 break;
287 case NETDEV_STAT_RECVDATA:
288 return ICONS_RECVDATA;
289 break;
290 default:
291 return NULL;
292 }
293 }
294
295 static char *select_icon_theme(gboolean plug, gboolean connected, int stat)
296 {
297 if (!plug)
298 return ICONS_DISCONNECT_THEME;
299
300 switch(stat) {
301 case NETDEV_STAT_NORMAL:
302 return ICONS_CONNECTED_THEME;
303 break;
304 case NETDEV_STAT_PROBLEM:
305 return ICONS_PROBLEM_THEME;
306 break;
307 case NETDEV_STAT_RENEW:
308 return ICONS_RENEW_THEME;
309 break;
310 case NETDEV_STAT_BOTHRS:
311 return ICONS_BOTHRS_THEME;
312 break;
313 case NETDEV_STAT_SENDDATA:
314 return ICONS_SENDDATA_THEME;
315 break;
316 case NETDEV_STAT_RECVDATA:
317 return ICONS_RECVDATA_THEME;
318 break;
319 default:
320 return NULL;
321 }
322 }
323
324 static void refresh_systray(netstat *ns, NETDEVLIST_PTR netdev_list)
325 {
326 NETDEVLIST_PTR ptr;
327 char *tooltip;
328
329 if (netdev_list==NULL) {
330 return;
331 }
332
333 ptr = netdev_list;
334 do {
335 if (!ptr->info.enable) {
336 if (ptr->info.status_icon!=NULL) {
337 set_statusicon_visible(ptr->info.status_icon, FALSE);
338 }
339 } else if (ptr->info.updated) {
340 if (!ptr->info.plug) {
341 if (ptr->info.wireless)
342 tooltip = g_strdup_printf("%s\n %s", ptr->info.ifname, _("Wireless Connection has no connectivity"));
343 else
344 tooltip = g_strdup_printf("%s\n %s", ptr->info.ifname, _("Network cable is plugged out"));
345 } else if (!ptr->info.connected)
346 tooltip = g_strdup_printf("%s\n %s", ptr->info.ifname, _("Connection has limited or no connectivity"));
347 else if (ptr->info.flags & IFF_POINTOPOINT)
348 tooltip = g_strdup_printf("%s\n %s\t%s\n %s\t%s\n %s\t%s\n\n %s(%s/%s)\n %lu/%lu %s\n %lu/%lu %s",
349 ptr->info.ifname,
350 _("IP Address:"), ptr->info.ipaddr,
351 _("Remote IP:"), ptr->info.dest,
352 _("Netmask:"), ptr->info.mask,
353 _("Activity"), _("Sent"),_("Received"),
354 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
355 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
356 else if (ptr->info.wireless)
357 tooltip = g_strdup_printf("%s(%s) - %s(%d%%)\n %s\t%s\n %s\t%s\n %s\t%s\n %s\t%s\n %s\t%s\n\n %s(%s/%s)\n %lu/%lu %s\n %lu/%lu %s",
358 ptr->info.ifname, _("Wireless"),
359 ptr->info.essid, ptr->info.quality,
360 _("Protocol:"), ptr->info.protocol,
361 _("IP Address:"), ptr->info.ipaddr,
362 _("Broadcast:"), ptr->info.bcast,
363 _("Netmask:"), ptr->info.mask,
364 _("HW Address:"), ptr->info.mac,
365 _("Activity"), _("Sent"), _("Received"),
366 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
367 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
368
369 else
370 tooltip = g_strdup_printf("%s\n %s\t%s\n %s\t%s\n %s\t%s\n %s\t%s\n\n %s(%s/%s)\n %lu/%lu %s\n %lu/%lu %s",
371 ptr->info.ifname,
372 _("IP Address:"), ptr->info.ipaddr,
373 _("Broadcast:"), ptr->info.bcast,
374 _("Netmask:"), ptr->info.mask,
375 _("HW Address:"), ptr->info.mac,
376 _("Activity"), _("Sent"), _("Received"),
377 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
378 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
379
380 /* status icon doesn't exist */
381 if (ptr->info.status_icon==NULL) {
382 netdev_info *ni;
383 ni = malloc(sizeof(netdev_info));
384 ni->ns = ns;
385 ni->netdev_list = ptr;
386
387 ptr->info.status_icon = create_statusicon(ns->mainw, select_icon(ptr->info.plug, ptr->info.connected, ptr->info.status), tooltip, select_icon_theme(ptr->info.plug, ptr->info.connected, ptr->info.status));
388 g_signal_connect(ptr->info.status_icon->main, "button_press_event", G_CALLBACK(menupopup), ni);
389 g_object_weak_ref(G_OBJECT(ptr->info.status_icon->main), g_free_weaknotify, ni);
390 } else {
391 set_statusicon_tooltips(ptr->info.status_icon, tooltip);
392 set_statusicon_image_from_file(ptr->info.status_icon, select_icon(ptr->info.plug, ptr->info.connected, ptr->info.status));
393 set_statusicon_visible(ptr->info.status_icon, TRUE);
394 }
395 g_free(tooltip);
396 }
397 ptr = ptr->next;
398 } while(ptr!=NULL);
399 }
400
401 static gboolean refresh_devstat(netstat *ns)
402 {
403 netproc_listener(ns->fnetd);
404 #ifdef DEBUG
405 netproc_print(ns->fnetd->netdevlist);
406 #endif
407 refresh_systray(ns, ns->fnetd->netdevlist);
408 netproc_devicelist_clear(&ns->fnetd->netdevlist);
409 return TRUE;
410 }
411
412 /* Plugin constructor */
413 static void netstat_destructor(Plugin *p)
414 {
415 netstat *ns = (netstat *) p->priv;
416
417 ENTER;
418 g_source_remove(ns->ttag);
419 netproc_netdevlist_clear(&ns->fnetd->netdevlist);
420 /* The widget is destroyed in plugin_stop().
421 gtk_widget_destroy(ns->mainw);
422 */
423 lxnm_close(ns->fnetd->lxnmchannel);
424 close(ns->fnetd->sockfd);
425 close(ns->fnetd->iwsockfd);
426 g_free(ns->fnetd);
427 g_free(ns->fixcmd);
428 g_free(ns);
429 RET();
430 }
431
432 static int netstat_constructor(Plugin *p, char **fp)
433 {
434 netstat *ns;
435 line s;
436
437 ENTER;
438 s.len = 256;
439 ns = g_new0(netstat, 1);
440 g_return_val_if_fail(ns != NULL, 0);
441 p->priv = ns;
442 ns->fixcmd = NULL;
443
444 if( fp ) {
445 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
446 if (s.type == LINE_NONE) {
447 ERR( "netstat: illegal token %s\n", s.str);
448 goto error;
449 }
450 if (s.type == LINE_VAR) {
451 if (!g_ascii_strcasecmp(s.t[0], "FixCommand"))
452 ns->fixcmd = g_strdup(s.t[1]);
453 else {
454 ERR( "netstat: unknown var %s\n", s.t[0]);
455 }
456 } else {
457 ERR( "netstat: illegal in this context %s\n", s.str);
458 goto error;
459 }
460 }
461 }
462
463 /* initializing */
464 ns->fnetd = malloc(sizeof(FNETD));
465 ns->fnetd->netdevlist = NULL;
466 ns->fnetd->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
467 ns->fnetd->iwsockfd = iw_sockets_open();
468 ns->fnetd->lxnmchannel = lxnm_socket();
469
470 /* main */
471 ns->mainw = p->panel->my_box_new(FALSE, 1);
472 gtk_widget_show_all(ns->mainw);
473
474 /* Initializing network device list*/
475 ns->fnetd->netdev_fp = netproc_open();
476 ns->fnetd->dev_count = netproc_netdevlist_clear(&ns->fnetd->netdevlist);
477 ns->fnetd->dev_count = netproc_scandevice(ns->fnetd->sockfd, ns->fnetd->iwsockfd, ns->fnetd->netdev_fp, &ns->fnetd->netdevlist);
478 netproc_close(ns->fnetd->netdev_fp);
479 refresh_systray(ns, ns->fnetd->netdevlist);
480
481 ns->ttag = g_timeout_add(NETSTAT_IFACE_POLL_DELAY, (GSourceFunc)refresh_devstat, ns);
482
483 p->pwid = gtk_event_box_new();
484 GTK_WIDGET_SET_FLAGS(p->pwid, GTK_NO_WINDOW);
485 gtk_container_add((GtkContainer*)p->pwid, ns->mainw);
486
487 RET(1);
488 error:
489 g_free(ns->fnetd);
490 g_free(ns->fixcmd);
491 g_free(ns);
492 RET(0);
493 }
494
495 static void orientation_changed(Plugin* p)
496 {
497 netstat *ns = (netstat *)p->priv;
498 GtkBox* newbox;
499 newbox = GTK_BOX(recreate_box(GTK_BOX(ns->mainw), p->panel->orientation));
500 if( GTK_WIDGET(newbox) != ns->mainw ) {
501 /* Since the old box has been destroyed,
502 we need to re-add the new box to the container */
503 ns->mainw = GTK_WIDGET(newbox);
504 gtk_container_add(GTK_CONTAINER(p->pwid), ns->mainw);
505 }
506 }
507
508 PluginClass netstat_plugin_class = {
509
510 PLUGINCLASS_VERSIONING,
511
512 type : "netstat",
513 name : N_("Manage Networks"),
514 version: "1.0",
515 description : N_("Monitor and Manage networks"),
516
517 constructor : netstat_constructor,
518 destructor : netstat_destructor,
519 panel_configuration_changed : orientation_changed,
520 config : NULL,
521 save : NULL
522 };
523
524 /* vim: set sw=4 sts=4 et : */