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