f1fb809433213cad0703ab372a66578db2c861e3
[debian/lxpanel.git] / plugins / netstat / netstat.c
1 /**
2 * Copyright (c) 2008-2014 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 "plugin.h"
38 #include "dbg.h"
39
40 /* 1 second */
41 #define NETSTAT_IFACE_POLL_DELAY 3000
42
43 static void* actionProcess(void *arg)
44 {
45 ENTER;
46 RET(GINT_TO_POINTER(system((char *)arg)));
47 }
48
49 /* menu handlers */
50 static void ethernet_repair(GtkWidget *widget, netdev_info *ni)
51 {
52 if (ni->ns->fixcmd) {
53 pthread_t actionThread;
54 pthread_attr_t attr;
55 char *fixcmd;
56
57 fixcmd = g_strdup_printf(ni->ns->fixcmd, ni->netdev_list->info.ifname);
58
59 /* Note: Better would be to join the thread, and thus get a status
60 * message. But detaching is currently simpler to implement. */
61 pthread_attr_init(&attr);
62 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
63 pthread_create(&actionThread, &attr, actionProcess, fixcmd);
64 pthread_attr_destroy(&attr);
65 } else {
66 lxnm_send_command(ni->ns->fnetd->lxnmchannel, LXNM_ETHERNET_REPAIR, ni->netdev_list->info.ifname);
67 }
68 }
69
70 static void ethernet_down(GtkWidget *widget, netdev_info *ni)
71 {
72 lxnm_send_command(ni->ns->fnetd->lxnmchannel, LXNM_ETHERNET_DOWN, ni->netdev_list->info.ifname);
73 }
74
75 static void wireless_connect(GtkWidget *widget, ap_setting *aps)
76 {
77 char *cmdargs;
78
79 /* without encryption */
80 if (!aps->apinfo->haskey) {
81 cmdargs = lxnm_wireless_command_make(aps->ifname, aps->apinfo->essid, aps->apinfo->apaddr, "",
82 aps->apinfo->en_method, aps->apinfo->key_mgmt,
83 aps->apinfo->group, aps->apinfo->pairwise);
84 lxnm_send_command(aps->gio, LXNM_WIRELESS_CONNECT, cmdargs);
85 g_free(cmdargs);
86 } else {
87 /* with encryption */
88 if (aps->ni->netdev_list->info.pg!=NULL)
89 passwd_gui_destroy(aps->ni->netdev_list->info.pg);
90
91 /* record information for password dialog */
92 ap_setting *aps_new;
93 ap_info *apinfo;
94 apinfo = malloc(sizeof(ap_info));
95 if (aps->apinfo->essid==NULL)
96 apinfo->essid = NULL;
97 else
98 apinfo->essid = g_strdup(aps->apinfo->essid);
99
100 apinfo->apaddr = g_strdup(aps->apinfo->apaddr);
101 apinfo->quality = aps->apinfo->quality;
102 apinfo->en_method = aps->apinfo->en_method;
103 apinfo->pairwise = aps->apinfo->pairwise;
104 apinfo->group = aps->apinfo->group;
105 apinfo->key_mgmt = aps->apinfo->key_mgmt;
106 apinfo->haskey = aps->apinfo->haskey;
107
108 aps_new = g_new0(ap_setting, 1);
109 aps_new->ni = aps->ni;
110 aps_new->gio = aps->gio;
111 aps_new->ifname = g_strdup(aps->ifname);
112 aps_new->apinfo = apinfo;
113 /* create dialog window for typing password */
114 aps->ni->netdev_list->info.pg = passwd_gui_new(aps_new);
115 //passwd_gui_set_style(aps->ni->netdev_list->info.pg, gtk_style_new());
116 }
117 }
118
119 static void
120 g_free_weaknotify(void *ptr, GObject *dummy)
121 {
122 g_free(ptr);
123 }
124
125 static GtkWidget *
126 wireless_menu(netdev_info *ni)
127 {
128 APLIST *aplist;
129 APLIST *ptr;
130 GtkWidget *menu;
131 GtkWidget *menu_item;
132 GtkWidget *wireless_label;
133
134 /* AP status widget */
135 GtkWidget *item_box;
136 GtkWidget *essid_label;
137 GtkWidget *lockicon;
138 GtkWidget *signal_quality;
139 gdouble quality_per;
140 ap_setting *aps;
141
142 /* create menu */
143 menu = gtk_menu_new();
144 g_signal_connect(menu, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL);
145
146 /* Scanning AP */
147 aplist = wireless_scanning(ni->ns->fnetd->iwsockfd, ni->netdev_list->info.ifname);
148 if (aplist!=NULL) {
149 /* release AP list after menu */
150 g_object_weak_ref(G_OBJECT(menu), wireless_aplist_free, aplist);
151 ptr = aplist;
152 do {
153 /* skip hidden AP with Encryption */
154 if (ptr->info->haskey&&ptr->info->essid==NULL) {
155 /* handle next AP */
156 ptr = ptr->next;
157 continue;
158 }
159
160 aps = g_new0(ap_setting, 1);
161 aps->ni = ni;
162 aps->gio = ni->ns->fnetd->lxnmchannel;
163 aps->ifname = ni->netdev_list->info.ifname;
164 aps->apinfo = ptr->info;
165
166 /* create a new item */
167 menu_item = gtk_menu_item_new();
168 item_box = gtk_hbox_new(FALSE, 0);
169
170 /* Encryption */
171 if (aps->apinfo->haskey) {
172 lockicon = gtk_image_new_from_file(ICONS_WL_LOCK);
173 gtk_box_pack_start(GTK_BOX(item_box), lockicon, FALSE, FALSE, 0);
174 }
175
176 /* ESSID */
177 if (aps->apinfo->essid==NULL)
178 essid_label = gtk_label_new(_("<Hidden Access Point>"));
179 else
180 essid_label = gtk_label_new(aps->apinfo->essid);
181
182 gtk_label_set_justify(GTK_LABEL(essid_label), GTK_JUSTIFY_LEFT);
183 gtk_misc_set_padding(GTK_MISC(essid_label), 2, 0);
184 gtk_box_pack_start(GTK_BOX(item_box), essid_label, TRUE, FALSE, 0);
185
186 /* Quality */
187 quality_per = (gdouble)((double)aps->apinfo->quality/100);
188 if (quality_per>1)
189 quality_per = 1;
190 else if (quality_per<0)
191 quality_per = 0;
192
193 signal_quality = gtk_progress_bar_new();
194 gtk_widget_set_size_request(signal_quality, 100, -1);
195 gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(signal_quality), GTK_PROGRESS_LEFT_TO_RIGHT);
196 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(signal_quality), quality_per);
197 gtk_box_pack_start(GTK_BOX(item_box), signal_quality, FALSE, FALSE, 0);
198
199 /* add this item to menu */
200 gtk_container_add(GTK_CONTAINER(menu_item), item_box);
201 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
202 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(wireless_connect), aps);
203 g_object_weak_ref(G_OBJECT(menu_item), g_free_weaknotify, aps);
204
205 /* handle next AP */
206 ptr = ptr->next;
207 } while(ptr!=NULL);
208 } else {
209 /* we do not found any wireless networks */
210 menu_item = gtk_menu_item_new();
211 wireless_label = gtk_label_new(_("Wireless Networks not found in range"));
212 gtk_label_set_justify(GTK_LABEL(wireless_label), GTK_JUSTIFY_LEFT);
213 gtk_widget_set_sensitive(GTK_WIDGET(wireless_label), FALSE);
214 gtk_container_add(GTK_CONTAINER(menu_item), wireless_label);
215 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
216 }
217
218 gtk_widget_show_all(menu);
219
220 return menu;
221 }
222
223 static gint menupopup(GtkWidget *widget, GdkEvent *event, netdev_info *ni)
224 {
225 GdkEventButton *event_button;
226
227 g_return_val_if_fail (event != NULL, FALSE);
228
229 if (event->type == GDK_BUTTON_PRESS) {
230 event_button = (GdkEventButton *) event;
231 if (event_button->button == 1) {
232 /* wireless device */
233 if (ni->netdev_list->info.wireless) {
234 gtk_menu_popup(GTK_MENU(wireless_menu(ni)), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
235 }
236 return TRUE;
237 } else if (event_button->button == 3) {
238 GtkWidget *menu;
239 GtkWidget *menu_item;
240
241 /* create menu */
242 menu = gtk_menu_new();
243
244 /* Repair */
245 menu_item = gtk_menu_item_new_with_label(_("Repair"));
246 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
247 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(ethernet_repair), ni);
248
249 /* interface down */
250 menu_item = gtk_menu_item_new_with_label(_("Disable"));
251 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
252 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(ethernet_down), ni);
253
254 gtk_widget_show_all(menu);
255
256 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event_button->button, event_button->time);
257 return TRUE;
258 }
259 }
260
261 return FALSE;
262 }
263
264 static char *select_icon(gboolean plug, gboolean connected, int stat)
265 {
266 if (!plug)
267 return ICONS_DISCONNECT;
268
269 switch(stat) {
270 case NETDEV_STAT_NORMAL:
271 return ICONS_CONNECTED;
272 break;
273 case NETDEV_STAT_PROBLEM:
274 return ICONS_PROBLEM;
275 break;
276 case NETDEV_STAT_RENEW:
277 return ICONS_RENEW;
278 break;
279 case NETDEV_STAT_BOTHRS:
280 return ICONS_BOTHRS;
281 break;
282 case NETDEV_STAT_SENDDATA:
283 return ICONS_SENDDATA;
284 break;
285 case NETDEV_STAT_RECVDATA:
286 return ICONS_RECVDATA;
287 break;
288 default:
289 return NULL;
290 }
291 }
292
293 static char *select_icon_theme(gboolean plug, gboolean connected, int stat)
294 {
295 if (!plug)
296 return ICONS_DISCONNECT_THEME;
297
298 switch(stat) {
299 case NETDEV_STAT_NORMAL:
300 return ICONS_CONNECTED_THEME;
301 break;
302 case NETDEV_STAT_PROBLEM:
303 return ICONS_PROBLEM_THEME;
304 break;
305 case NETDEV_STAT_RENEW:
306 return ICONS_RENEW_THEME;
307 break;
308 case NETDEV_STAT_BOTHRS:
309 return ICONS_BOTHRS_THEME;
310 break;
311 case NETDEV_STAT_SENDDATA:
312 return ICONS_SENDDATA_THEME;
313 break;
314 case NETDEV_STAT_RECVDATA:
315 return ICONS_RECVDATA_THEME;
316 break;
317 default:
318 return NULL;
319 }
320 }
321
322 static void refresh_systray(netstat *ns, NETDEVLIST_PTR netdev_list)
323 {
324 NETDEVLIST_PTR ptr;
325 char *tooltip;
326
327 if (netdev_list==NULL) {
328 return;
329 }
330
331 ptr = netdev_list;
332 do {
333 if (!ptr->info.enable) {
334 if (ptr->info.status_icon!=NULL) {
335 set_statusicon_visible(ptr->info.status_icon, FALSE);
336 }
337 } else if (ptr->info.updated) {
338 if (!ptr->info.plug) {
339 if (ptr->info.wireless)
340 tooltip = g_strdup_printf("%s\n %s", ptr->info.ifname, _("Wireless Connection has no connectivity"));
341 else
342 tooltip = g_strdup_printf("%s\n %s", ptr->info.ifname, _("Network cable is plugged out"));
343 } else if (!ptr->info.connected)
344 tooltip = g_strdup_printf("%s\n %s", ptr->info.ifname, _("Connection has limited or no connectivity"));
345 else if (ptr->info.flags & IFF_POINTOPOINT)
346 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",
347 ptr->info.ifname,
348 _("IP Address:"), ptr->info.ipaddr,
349 _("Remote IP:"), ptr->info.dest,
350 _("Netmask:"), ptr->info.mask,
351 _("Activity"), _("Sent"),_("Received"),
352 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
353 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
354 else if (ptr->info.wireless)
355 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",
356 ptr->info.ifname, _("Wireless"),
357 ptr->info.essid, ptr->info.quality,
358 _("Protocol:"), ptr->info.protocol,
359 _("IP Address:"), ptr->info.ipaddr,
360 _("Broadcast:"), ptr->info.bcast,
361 _("Netmask:"), ptr->info.mask,
362 _("HW Address:"), ptr->info.mac,
363 _("Activity"), _("Sent"), _("Received"),
364 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
365 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
366
367 else
368 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",
369 ptr->info.ifname,
370 _("IP Address:"), ptr->info.ipaddr,
371 _("Broadcast:"), ptr->info.bcast,
372 _("Netmask:"), ptr->info.mask,
373 _("HW Address:"), ptr->info.mac,
374 _("Activity"), _("Sent"), _("Received"),
375 ptr->info.trans_bytes, ptr->info.recv_bytes, _("bytes"),
376 ptr->info.trans_packets, ptr->info.recv_packets, _("packets"));
377
378 /* status icon doesn't exist */
379 if (ptr->info.status_icon==NULL) {
380 netdev_info *ni;
381 ni = malloc(sizeof(netdev_info));
382 ni->ns = ns;
383 ni->netdev_list = ptr;
384
385 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));
386 g_signal_connect(ptr->info.status_icon->main, "button-press-event", G_CALLBACK(menupopup), ni);
387 g_object_weak_ref(G_OBJECT(ptr->info.status_icon->main), g_free_weaknotify, ni);
388 } else {
389 set_statusicon_tooltips(ptr->info.status_icon, tooltip);
390 set_statusicon_image_from_file(ptr->info.status_icon, select_icon(ptr->info.plug, ptr->info.connected, ptr->info.status));
391 set_statusicon_visible(ptr->info.status_icon, TRUE);
392 }
393 g_free(tooltip);
394 }
395 ptr = ptr->next;
396 } while(ptr!=NULL);
397 }
398
399 static gboolean refresh_devstat(netstat *ns)
400 {
401 if (g_source_is_destroyed(g_main_current_source()))
402 return FALSE;
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(gpointer user_data)
414 {
415 netstat *ns = (netstat *) user_data;
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 GtkWidget *netstat_constructor(LXPanel *panel, config_setting_t *settings)
433 {
434 netstat *ns;
435 const char *tmp;
436 GtkWidget *p;
437
438 ENTER;
439 ns = g_new0(netstat, 1);
440 g_return_val_if_fail(ns != NULL, NULL);
441 /* apply config */
442 if (config_setting_lookup_string(settings, "FixCommand", &tmp))
443 ns->fixcmd = g_strdup(tmp);
444
445 /* initializing */
446 ns->fnetd = malloc(sizeof(FNETD));
447 ns->fnetd->netdevlist = NULL;
448 ns->fnetd->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
449 ns->fnetd->iwsockfd = iw_sockets_open();
450 ns->fnetd->lxnmchannel = lxnm_socket();
451
452 /* main */
453 ns->mainw = panel_box_new(panel, FALSE, 1);
454 gtk_widget_show_all(ns->mainw);
455
456 /* Initializing network device list*/
457 ns->fnetd->netdev_fp = netproc_open();
458 ns->fnetd->dev_count = netproc_netdevlist_clear(&ns->fnetd->netdevlist);
459 ns->fnetd->dev_count = netproc_scandevice(ns->fnetd->sockfd, ns->fnetd->iwsockfd, ns->fnetd->netdev_fp, &ns->fnetd->netdevlist);
460 netproc_close(ns->fnetd->netdev_fp);
461 refresh_systray(ns, ns->fnetd->netdevlist);
462
463 ns->ttag = g_timeout_add(NETSTAT_IFACE_POLL_DELAY, (GSourceFunc)refresh_devstat, ns);
464
465 p = gtk_event_box_new();
466 lxpanel_plugin_set_data(p, ns, netstat_destructor);
467 gtk_widget_set_has_window(p, FALSE);
468 gtk_container_add((GtkContainer*)p, ns->mainw);
469
470 RET(p);
471 }
472
473 static void orientation_changed(LXPanel *panel, GtkWidget *p)
474 {
475 netstat *ns = lxpanel_plugin_get_data(p);
476
477 gtk_orientable_set_orientation(GTK_ORIENTABLE(ns->mainw),
478 panel_get_orientation(panel));
479 }
480
481 FM_DEFINE_MODULE(lxpanel_gtk, netstat)
482
483 LXPanelPluginInit fm_module_init_lxpanel_gtk = {
484 .name = N_("Manage Networks"),
485 .description = N_("Monitor and Manage networks"),
486
487 .new_instance = netstat_constructor,
488 .reconfigure = orientation_changed,
489 };
490
491 /* vim: set sw=4 sts=4 et : */