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