1 /* $Id$ */
2 /* Copyright (c) 2014-2015 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Panel */
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, version 3 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */
15
16
17
18 #include <sys/types.h>
19 #include <sys/ioctl.h>
20 #include <sys/socket.h>
21 #include <stdlib.h>
22 #ifdef DEBUG
23 # include <stdio.h>
24 #endif
25 #include <string.h>
26 #include <errno.h>
27 #ifdef __NetBSD__
28 # include <ifaddrs.h>
29 #endif
30 #include <libintl.h>
31 #include <net/if.h>
32 #include <System.h>
33 #include "Panel/applet.h"
34 #define _(string) gettext(string)
35
36
37 /* Network */
38 /* private */
39 /* types */
40 typedef struct _NetworkInterface
41 {
42 String * name;
43 unsigned int flags;
44 unsigned long ipackets;
45 unsigned long opackets;
46 unsigned long ibytes;
47 unsigned long obytes;
48 GtkWidget * widget;
49 gboolean updated;
50 } NetworkInterface;
51
52 typedef struct _PanelApplet
53 {
54 PanelAppletHelper * helper;
55 guint source;
56 int fd;
57 NetworkInterface * interfaces;
58 size_t interfaces_cnt;
59
60 /* widgets */
61 GtkWidget * widget;
62 GtkIconSize iconsize;
63 GtkWidget * pr_box;
64 #ifdef IFF_LOOPBACK
65 GtkWidget * pr_loopback;
66 #endif
67 #ifdef IFF_UP
68 GtkWidget * pr_showdown;
69 #endif
70 } Network;
71
72
73 /* prototypes */
74 /* plug-in */
75 static Network * _network_init(PanelAppletHelper * helper, GtkWidget ** widget);
76 static void _network_destroy(Network * network);
77
78 static GtkWidget * _network_settings(Network * network, gboolean apply,
79 gboolean reset);
80
81 /* useful */
82 static void _network_refresh(Network * network);
83
84 /* callbacks */
85 static gboolean _network_on_timeout(gpointer data);
86
87 /* NetworkInterface */
88 static int _networkinterface_init(NetworkInterface * ni, char const * name,
89 unsigned int flags);
90 static void _networkinterface_destroy(NetworkInterface * ni);
91 static void _networkinterface_update(NetworkInterface * ni, char const * icon,
92 GtkIconSize iconsize, gboolean active, unsigned int flags,
93 gboolean updated, char const * tooltip);
94
95
96 /* public */
97 /* variables */
98 PanelAppletDefinition applet =
99 {
100 "Network",
101 "network-idle",
102 NULL,
103 _network_init,
104 _network_destroy,
105 _network_settings,
106 FALSE,
107 TRUE
108 };
109
110
111 /* private */
112 /* functions */
113 /* network_init */
_network_init(PanelAppletHelper * helper,GtkWidget ** widget)114 static Network * _network_init(PanelAppletHelper * helper, GtkWidget ** widget)
115 {
116 const unsigned int timeout = 500;
117 Network * network;
118 GtkOrientation orientation;
119
120 if((network = object_new(sizeof(*network))) == NULL)
121 return NULL;
122 network->helper = helper;
123 orientation = panel_window_get_orientation(helper->window);
124 #if GTK_CHECK_VERSION(3, 0, 0)
125 network->widget = gtk_box_new(orientation, 0);
126 #else
127 network->widget = (orientation == GTK_ORIENTATION_HORIZONTAL)
128 ? gtk_hbox_new(TRUE, 0) : gtk_vbox_new(TRUE, 0);
129 #endif
130 network->iconsize = panel_window_get_icon_size(helper->window);
131 network->pr_box = NULL;
132 gtk_widget_show(network->widget);
133 network->source = g_timeout_add(timeout, _network_on_timeout, network);
134 if((network->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
135 {
136 error_set("%s: %s: %s", applet.name, "socket", strerror(errno));
137 network->helper->error(NULL, error_get(NULL), 1);
138 }
139 network->interfaces = NULL;
140 network->interfaces_cnt = 0;
141 *widget = network->widget;
142 _network_refresh(network);
143 return network;
144 }
145
146
147 /* network_destroy */
_network_destroy(Network * network)148 static void _network_destroy(Network * network)
149 {
150 size_t i;
151
152 for(i = 0; i < network->interfaces_cnt; i++)
153 _networkinterface_destroy(&network->interfaces[i]);
154 free(network->interfaces);
155 if(network->fd >= 0)
156 close(network->fd);
157 if(network->source != 0)
158 g_source_remove(network->source);
159 gtk_widget_destroy(network->widget);
160 object_delete(network);
161 }
162
163
164 /* useful */
165 /* network_refresh */
166 static void _refresh_interface(Network * network, char const * name,
167 unsigned int flags);
168 static int _refresh_interface_add(Network * network, char const * name,
169 unsigned int flags);
170 static void _refresh_interface_flags(Network * network, NetworkInterface * ni,
171 unsigned int flags);
172 static void _refresh_purge(Network * network);
173 static void _refresh_reset(Network * network);
174
_network_refresh(Network * network)175 static void _network_refresh(Network * network)
176 {
177 char const * p;
178 #ifdef __NetBSD__
179 struct ifaddrs * ifa;
180 struct ifaddrs * ifp;
181 #endif
182
183 if((p = network->helper->config_get(network->helper->panel, "network",
184 "interface")) != NULL)
185 {
186 /* FIXME obtain some flags if possible */
187 #ifdef IFF_UP
188 _refresh_interface(network, p, IFF_UP);
189 #else
190 _refresh_interface(network, p, 0);
191 #endif
192 return;
193 }
194 #ifdef __NetBSD__
195 if(getifaddrs(&ifa) != 0)
196 return;
197 _refresh_reset(network);
198 for(ifp = ifa; ifp != NULL; ifp = ifp->ifa_next)
199 {
200 _refresh_interface(network, ifp->ifa_name, ifp->ifa_flags);
201 /* XXX avoid repeated entries */
202 for(; ifp->ifa_next != NULL && strcmp(ifp->ifa_name,
203 ifp->ifa_next->ifa_name) == 0;
204 ifp = ifp->ifa_next);
205 }
206 /* FIXME also remove/disable the interfaces not listed */
207 freeifaddrs(ifa);
208 _refresh_purge(network);
209 #endif
210 }
211
_refresh_interface(Network * network,char const * name,unsigned int flags)212 static void _refresh_interface(Network * network, char const * name,
213 unsigned int flags)
214 {
215 size_t i;
216 int res;
217
218 #ifdef DEBUG
219 fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name);
220 #endif
221 for(i = 0; i < network->interfaces_cnt; i++)
222 if(strcmp(network->interfaces[i].name, name) == 0)
223 break;
224 /* FIXME really implement */
225 if(i == network->interfaces_cnt)
226 /* XXX assumes network->interfaces[i] will be correct */
227 if((res = _refresh_interface_add(network, name, flags)) != 0)
228 {
229 if(res < 0)
230 network->helper->error(NULL, error_get(NULL),
231 1);
232 return;
233 }
234 _refresh_interface_flags(network, &network->interfaces[i], flags);
235 }
236
_refresh_interface_add(Network * network,char const * name,unsigned int flags)237 static int _refresh_interface_add(Network * network, char const * name,
238 unsigned int flags)
239 {
240 NetworkInterface * p;
241 #if defined(IFF_LOOPBACK) || defined(IFF_UP)
242 char const * q;
243 #endif
244
245 #ifdef IFF_LOOPBACK
246 if(flags & IFF_LOOPBACK)
247 {
248 q = network->helper->config_get(network->helper->panel,
249 "network", "loopback");
250 if(q == NULL || strtol(q, NULL, 10) == 0)
251 /* ignore the interface */
252 return 1;
253 }
254 #endif
255 #ifdef IFF_UP
256 if((flags & IFF_UP) == 0)
257 {
258 q = network->helper->config_get(network->helper->panel,
259 "network", "showdown");
260 if(q != NULL && strtol(q, NULL, 10) == 0)
261 /* ignore the interface */
262 return 1;
263 }
264 #endif
265 if((p = realloc(network->interfaces, sizeof(*p)
266 * (network->interfaces_cnt + 1)))
267 == NULL)
268 return -error_set_code(1, "%s: %s", applet.name,
269 strerror(errno));
270 network->interfaces = p;
271 p = &network->interfaces[network->interfaces_cnt];
272 if(_networkinterface_init(p, name, flags) != 0)
273 return -1;
274 _refresh_interface_flags(network, p, flags);
275 gtk_box_pack_start(GTK_BOX(network->widget), p->widget, FALSE, TRUE, 0);
276 gtk_widget_show(p->widget);
277 network->interfaces_cnt++;
278 return 0;
279 }
280
_refresh_interface_delete(Network * network,size_t i)281 static void _refresh_interface_delete(Network * network, size_t i)
282 {
283 NetworkInterface * ni = &network->interfaces[i];
284
285 _networkinterface_destroy(ni);
286 network->interfaces_cnt--;
287 memmove(&network->interfaces[i], &network->interfaces[i + 1],
288 sizeof(*ni) * (network->interfaces_cnt - i));
289 /* XXX realloc() network->interfaces to free some memory */
290 }
291
_refresh_interface_flags(Network * network,NetworkInterface * ni,unsigned int flags)292 static void _refresh_interface_flags(Network * network, NetworkInterface * ni,
293 unsigned int flags)
294 {
295 gboolean active = TRUE;
296 char const * icon = "network-offline";
297 #if defined(SIOCGIFDATA) && !defined(__DragonFly__)
298 struct ifdatareq ifdr;
299 # if GTK_CHECK_VERSION(2, 12, 0)
300 unsigned long ibytes;
301 unsigned long obytes;
302 # endif
303 #endif
304 char tooltip[128] = "";
305
306 #ifdef IFF_UP
307 if((flags & IFF_UP) != IFF_UP)
308 active = FALSE;
309 else
310 #endif
311 {
312 #if defined(SIOCGIFDATA) && !defined(__DragonFly__)
313 /* XXX ignore errors */
314 memset(&ifdr, 0, sizeof(ifdr));
315 strncpy(ifdr.ifdr_name, ni->name, sizeof(ifdr.ifdr_name));
316 if(ioctl(network->fd, SIOCGIFDATA, &ifdr) == -1)
317 network->helper->error(NULL, "SIOCGIFDATA", 1);
318 else
319 {
320 if(ifdr.ifdr_data.ifi_ipackets > ni->ipackets)
321 icon = (ifdr.ifdr_data.ifi_opackets
322 > ni->opackets)
323 ? "network-transmit-receive"
324 : "network-receive";
325 else if(ifdr.ifdr_data.ifi_opackets > ni->opackets)
326 icon = "network-transmit";
327 # ifdef LINK_STATE_DOWN
328 else if(ifdr.ifdr_data.ifi_link_state
329 == LINK_STATE_DOWN)
330 icon = "network-offline";
331 # endif
332 else
333 icon = "network-idle";
334 # if GTK_CHECK_VERSION(2, 12, 0)
335 ibytes = (ifdr.ifdr_data.ifi_ibytes >= ni->ibytes)
336 ? ifdr.ifdr_data.ifi_ibytes - ni->ibytes
337 : ULONG_MAX - ni->ibytes
338 + ifdr.ifdr_data.ifi_ibytes;
339 obytes = (ifdr.ifdr_data.ifi_obytes >= ni->obytes)
340 ? ifdr.ifdr_data.ifi_obytes - ni->obytes
341 : ULONG_MAX - ni->obytes
342 + ifdr.ifdr_data.ifi_obytes;
343 snprintf(tooltip, sizeof(tooltip),
344 _("%s\nIn: %lu kB/s\nOut: %lu kB/s"),
345 ni->name, ibytes / 512, obytes / 512);
346 # endif
347 ni->ipackets = ifdr.ifdr_data.ifi_ipackets;
348 ni->opackets = ifdr.ifdr_data.ifi_opackets;
349 ni->ibytes = ifdr.ifdr_data.ifi_ibytes;
350 ni->obytes = ifdr.ifdr_data.ifi_obytes;
351 }
352 #endif
353 }
354 _networkinterface_update(ni, icon, network->iconsize, active, flags,
355 TRUE, (tooltip[0] != '\0') ? tooltip : NULL);
356 }
357
_refresh_purge(Network * network)358 static void _refresh_purge(Network * network)
359 {
360 size_t i;
361
362 for(i = 0; i < network->interfaces_cnt;)
363 if(network->interfaces[i].updated == FALSE)
364 _refresh_interface_delete(network, i);
365 else
366 i++;
367 }
368
_refresh_reset(Network * network)369 static void _refresh_reset(Network * network)
370 {
371 size_t i;
372
373 for(i = 0; i < network->interfaces_cnt; i++)
374 network->interfaces[i].updated = FALSE;
375 }
376
377
378 /* network_settings */
379 static void _settings_apply(Network * network, PanelAppletHelper * helper);
380 static void _settings_reset(Network * network, PanelAppletHelper * helper);
381
_network_settings(Network * network,gboolean apply,gboolean reset)382 static GtkWidget * _network_settings(Network * network, gboolean apply,
383 gboolean reset)
384 {
385 PanelAppletHelper * helper = network->helper;
386
387 if(network->pr_box == NULL)
388 {
389 #if GTK_CHECK_VERSION(3, 0, 0)
390 network->pr_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
391 #else
392 network->pr_box = gtk_vbox_new(TRUE, 4);
393 #endif
394 #ifdef IFF_LOOPBACK
395 network->pr_loopback = gtk_check_button_new_with_label(
396 _("Show local interfaces"));
397 gtk_box_pack_start(GTK_BOX(network->pr_box),
398 network->pr_loopback, FALSE, TRUE, 0);
399 #endif
400 #ifdef IFF_UP
401 network->pr_showdown = gtk_check_button_new_with_label(
402 _("Show the interfaces disabled"));
403 gtk_box_pack_start(GTK_BOX(network->pr_box),
404 network->pr_showdown, FALSE, TRUE, 0);
405 #endif
406 gtk_widget_show_all(network->pr_box);
407 reset = TRUE;
408 }
409 if(reset == TRUE)
410 _settings_reset(network, helper);
411 if(apply == TRUE)
412 _settings_apply(network, helper);
413 return network->pr_box;
414 }
415
_settings_apply(Network * network,PanelAppletHelper * helper)416 static void _settings_apply(Network * network, PanelAppletHelper * helper)
417 {
418 #if defined(IFF_LOOPBACK) || defined(IFF_UP)
419 gboolean active;
420 #endif
421
422 #ifdef IFF_LOOPBACK
423 active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
424 network->pr_loopback));
425 helper->config_set(helper->panel, "network", "loopback",
426 active ? "1" : "0");
427 #endif
428 #ifdef IFF_UP
429 active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
430 network->pr_showdown));
431 helper->config_set(helper->panel, "network", "showdown",
432 active ? "1" : "0");
433 #endif
434 _network_refresh(network);
435 }
436
_settings_reset(Network * network,PanelAppletHelper * helper)437 static void _settings_reset(Network * network, PanelAppletHelper * helper)
438 {
439 #ifndef EMBEDDED
440 # ifdef IFF_LOOPBACK
441 gboolean loopback = TRUE;
442 # endif
443 # ifdef IFF_UP
444 gboolean showdown = TRUE;
445 # endif
446 #else
447 # ifdef IFF_LOOPBACK
448 gboolean loopback = FALSE;
449 # endif
450 # ifdef IFF_UP
451 gboolean showdown = FALSE;
452 # endif
453 #endif
454 #if defined(IFF_LOOPBACK) || defined(IFF_UP)
455 char const * p;
456 #endif
457
458 #ifdef IFF_LOOPBACK
459 if((p = helper->config_get(helper->panel, "network", "loopback"))
460 != NULL)
461 loopback = strtol(p, NULL, 10) ? TRUE : FALSE;
462 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(network->pr_loopback),
463 loopback);
464 #endif
465 #ifdef IFF_UP
466 if((p = helper->config_get(helper->panel, "network", "showdown"))
467 != NULL)
468 showdown = strtol(p, NULL, 10) ? TRUE : FALSE;
469 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(network->pr_showdown),
470 showdown);
471 #endif
472 }
473
474
475 /* callbacks */
476 /* network_on_timeout */
_network_on_timeout(gpointer data)477 static gboolean _network_on_timeout(gpointer data)
478 {
479 const unsigned int timeout = 500;
480 Network * network = data;
481
482 _network_refresh(network);
483 network->source = g_timeout_add(timeout, _network_on_timeout, network);
484 return FALSE;
485 }
486
487
488 /* NetworkInterface */
489 /* networkinterface_init */
_networkinterface_init(NetworkInterface * ni,char const * name,unsigned int flags)490 static int _networkinterface_init(NetworkInterface * ni, char const * name,
491 unsigned int flags)
492 {
493 if((ni->name = string_new(name)) == NULL)
494 return -1;
495 ni->flags = flags;
496 ni->ipackets = 0;
497 ni->opackets = 0;
498 ni->ibytes = 0;
499 ni->obytes = 0;
500 ni->widget = gtk_image_new();
501 #if GTK_CHECK_VERSION(2, 12, 0)
502 gtk_widget_set_tooltip_text(ni->widget, name);
503 #endif
504 ni->updated = FALSE;
505 return 0;
506 }
507
508
509 /* networkinterface_destroy */
_networkinterface_destroy(NetworkInterface * ni)510 static void _networkinterface_destroy(NetworkInterface * ni)
511 {
512 string_delete(ni->name);
513 gtk_widget_destroy(ni->widget);
514 }
515
516
517 /* networkinterface_update */
_networkinterface_update(NetworkInterface * ni,char const * icon,GtkIconSize iconsize,gboolean active,unsigned int flags,gboolean updated,char const * tooltip)518 static void _networkinterface_update(NetworkInterface * ni, char const * icon,
519 GtkIconSize iconsize, gboolean active, unsigned int flags,
520 gboolean updated, char const * tooltip)
521 {
522 gtk_image_set_from_icon_name(GTK_IMAGE(ni->widget), icon, iconsize);
523 #ifdef EMBEDDED
524 if(active)
525 gtk_widget_show(ni->widget);
526 else
527 gtk_widget_hide(ni->widget);
528 #else
529 gtk_widget_set_sensitive(ni->widget, active);
530 #endif
531 #if GTK_CHECK_VERSION(2, 12, 0)
532 if(tooltip != NULL)
533 gtk_widget_set_tooltip_text(ni->widget, tooltip);
534 #endif
535 ni->flags = flags;
536 ni->updated = updated;
537 }
538