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