1 /*
2  * net usage plugin to fbpanel
3  *
4  * Copyright (C) 2004 by Alexandre Pereira da Silva <alexandre.pereira@poli.usp.br>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 /*A little bug fixed by Mykola <mykola@2ka.mipt.ru>:) */
22 /* FreeBSD code borrowed from patches to the lxpanel port */
23 
24 
25 
26 #include "../chart/chart.h"
27 
28 #if defined __FreeBSD__ || defined __DragonFly__
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/ioctl.h>
33 #include <ifaddrs.h>
34 #include <net/if.h>
35 #include <net/if_media.h>
36 #include <net/if_var.h>
37 #endif
38 
39 #include <stdlib.h>
40 #include <string.h>
41 
42 //#define DEBUGPRN
43 #include "dbg.h"
44 
45 
46 #define CHECK_PERIOD   2 /* second */
47 
48 /* net.c */
49 struct net_stat {
50     gulong tx, rx;
51 };
52 
53 typedef struct {
54     chart_priv chart;
55     struct net_stat net_prev;
56     int timer;
57     char *iface;
58     gint max_tx;
59     gint max_rx;
60     gulong max;
61     gchar *colors[2];
62 } net_priv;
63 
64 static chart_class *k;
65 
66 
67 static void net_destructor(plugin_instance *p);
68 
69 
70 #if defined __linux__
71 static int
net_get_load(net_priv * c)72 net_get_load(net_priv *c)
73 {
74     struct net_stat net, net_diff;
75     FILE *stat;
76     float total[2];
77     char buf[256], *s = NULL;
78 
79     ENTER;
80     memset(&net, 0, sizeof(net));
81     memset(&net_diff, 0, sizeof(net_diff));
82     stat = fopen("/proc/net/dev", "r");
83     if(!stat)
84         goto end;
85     fgets(buf, 256, stat);
86     fgets(buf, 256, stat);
87 
88     while (!s && !feof(stat) && fgets(buf, 256, stat))
89         s = g_strrstr(buf, c->iface);
90     fclose(stat);
91     if (!s)
92         goto end;
93     s = g_strrstr(s, ":");
94     if (!s)
95         goto end;
96     s++;
97     if (sscanf(s,
98             "%lu  %*d     %*d  %*d  %*d  %*d   %*d        %*d       %lu",
99             &net.rx, &net.tx)!= 2) {
100         DBG("can't read %s statistics\n", c->iface);
101         goto end;
102     }
103     net_diff.tx = ((net.tx - c->net_prev.tx) >> 10) / CHECK_PERIOD;
104     net_diff.rx = ((net.rx - c->net_prev.rx) >> 10) / CHECK_PERIOD;
105 end:
106 
107     c->net_prev = net;
108     total[0] = (float)(net_diff.tx) / c->max;
109     total[1] = (float)(net_diff.rx) / c->max;
110     DBG("%f %ul %ul\n", total, net_diff.tx, net_diff.rx);
111     k->add_tick(&c->chart, total);
112     g_snprintf(buf, sizeof(buf), "<b>%s:</b>\nD %lu Kbs, U %lu Kbs",
113         c->iface, net_diff.rx, net_diff.tx);
114     gtk_widget_set_tooltip_markup(((plugin_instance *)c)->pwid, buf);
115     RET(TRUE);
116 
117 }
118 #elif defined __FreeBSD__ || defined __DragonFly__
119 static inline	gboolean
parse_stats(char * buf,int prx_idx,int ptx_idx,gulong * in_packets,gulong * out_packets,int brx_idx,int btx_idx,gulong * in_bytes,gulong * out_bytes)120 parse_stats(char *buf,
121 	    int prx_idx,
122 	    int ptx_idx,
123 	    gulong * in_packets,
124 	    gulong * out_packets,
125 	    int brx_idx,
126 	    int btx_idx,
127 	    gulong * in_bytes,
128 	    gulong * out_bytes)
129 {
130 	char           *p;
131 	int		i;
132 
133 	p = strtok(buf, " \t\n");
134 	for (i = 0; p; i++, p = strtok(NULL, " \t\n")) {
135 		if (i == prx_idx)
136 			*in_packets = g_ascii_strtoull(p, NULL, 10);
137 		if (i == ptx_idx)
138 			*out_packets = g_ascii_strtoull(p, NULL, 10);
139 		if (i == brx_idx)
140 			*in_bytes = g_ascii_strtoull(p, NULL, 10);
141 		if (i == btx_idx)
142 			*out_bytes = g_ascii_strtoull(p, NULL, 10);
143 	}
144 
145 	if (i <= prx_idx || i <= ptx_idx || i <= brx_idx || i <= btx_idx)
146 		return FALSE;
147 
148 	return TRUE;
149 }
150 
151 static inline void
parse_header(char * buf,int * prx_idx,int * ptx_idx,int * brx_idx,int * btx_idx)152 parse_header(char *buf,
153 	     int *prx_idx,
154 	     int *ptx_idx,
155 	     int *brx_idx,
156 	     int *btx_idx)
157 {
158 	char           *p;
159 	int		i;
160 
161 	*prx_idx = *ptx_idx = -1;
162 	*brx_idx = *btx_idx = -1;
163 
164 	p = strtok(buf, " \n\t");
165 	for (i = 0; p; i++, p = strtok(NULL, " \t\n")) {
166 		if (!strcmp(p, "Ipkts")) {
167 			*prx_idx = i;
168 		} else if (!strcmp(p, "Ibytes")) {
169 			*brx_idx = i;
170 		} else if (!strcmp(p, "Opkts")) {
171 			*ptx_idx = i;
172 		} else if (!strcmp(p, "Obytes")) {
173 			*btx_idx = i;
174 		}
175 	}
176 }
177 static int
net_get_load(net_priv * c)178 net_get_load(net_priv * c)
179 {
180 	struct net_stat	net, net_diff;
181 	float		total[2];
182 	GError         *error;
183 	char           *command_line;
184 	char          **argv;
185 	char           *error_message = NULL;
186 	int		pipe_out;
187 	gulong		in_packets = -1;
188 	gulong		out_packets = -1;
189 	gulong		in_bytes = -1;
190 	gulong		out_bytes = -1;
191 	char		tooltip[256];
192 
193 	ENTER;
194 	error = NULL;
195 	command_line = g_strdup_printf("/usr/bin/netstat -n -I %s -b -f inet", c->iface);
196 	DBG(command_line);
197 	if (!g_shell_parse_argv(command_line, NULL, &argv, &error)) {
198 		error_message = g_strdup_printf("Could not parse command line '%s': %s",
199 						command_line,
200 						error->message);
201 		DBG(error_message);
202 		g_error_free(error);
203 		g_free(command_line);
204 		RET(0);
205 	}
206 	g_free(command_line);
207 
208 	error = NULL;
209 	if (g_spawn_async_with_pipes(NULL,
210 				     argv,
211 				     NULL,
212 				     0,
213 				     NULL,
214 				     NULL,
215 				     NULL,
216 				     NULL,
217 				     &pipe_out,
218 				     NULL,
219 				     &error)) {
220 		GIOChannel     *channel;
221 		char           *buf;
222 		int		prx_idx   , ptx_idx;
223 		int		brx_idx   , btx_idx;
224 
225 		channel = g_io_channel_unix_new(pipe_out);
226 
227 		g_io_channel_read_line(channel, &buf, NULL, NULL, NULL);
228 		parse_header(buf, &prx_idx, &ptx_idx, &brx_idx, &btx_idx);
229 		g_free(buf);
230 
231 		if (prx_idx == -1 || ptx_idx == -1 ||
232 		    brx_idx == -1 || btx_idx == -1) {
233 			error_message = g_strdup("Could not parse 'netstat' output. Unknown format");
234 			DBG(error_message);
235 			goto error_shutdown;
236 		}
237 		g_io_channel_read_line(channel, &buf, NULL, NULL, NULL);
238 
239 		if (!parse_stats(buf,
240 				 prx_idx, ptx_idx, &in_packets, &out_packets,
241 				 brx_idx, btx_idx, &in_bytes, &out_bytes)) {
242 			error_message = g_strdup_printf("Could not parse interface statistics from '%s'. "
243 							"prx_idx = %d; ptx_idx = %d; brx_idx = %d; btx_idx = %d;",
244 				   buf, prx_idx, ptx_idx, brx_idx, btx_idx);
245 			DBG(error_message);
246 			goto error_shutdown;
247 		} else if (in_packets == -1 || out_packets == -1 || in_bytes == -1 || out_bytes == -1) {
248 			error_message = g_strdup_printf("Could not obtain information on interface '%s' from netstat",
249 							c->iface);
250 			DBG(error_message);
251 			goto error_shutdown;
252 		}
253 		net.tx = out_bytes;
254 		net.rx = in_bytes;
255 
256 		net_diff.tx = ((net.tx - c->net_prev.tx) >> 10) / CHECK_PERIOD;
257 		net_diff.rx = ((net.rx - c->net_prev.rx) >> 10) / CHECK_PERIOD;
258 
259 		c->net_prev = net;
260 		total[0] = (float)(net_diff.tx) / c->max;
261 		total[1] = (float)(net_diff.rx) / c->max;
262 		DBG("%f %ul %ul\n", total, net_diff.tx, net_diff.rx);
263 		k->add_tick(&c->chart, total);
264 		g_snprintf(tooltip, sizeof(tooltip), "<b>%s:</b>\nD %lu Kbs, U %lu Kbs",
265 			   c->iface, net_diff.rx, net_diff.tx);
266 		gtk_widget_set_tooltip_markup(((plugin_instance *) c)->pwid, tooltip);
267 
268 error_shutdown:
269 		g_free(buf);
270 		g_io_channel_unref(channel);
271 		close(pipe_out);
272 	} else {
273 		error_message = g_strdup_printf("Error running /usr/bin/netstat for '%s': %s",
274 						c->iface, error->message);
275 		g_error_free(error);
276 	}
277 
278 	g_strfreev(argv);
279 
280 	RET(TRUE);
281 
282 }
283 #else
284 
285 static int
net_get_load(net_priv * c)286 net_get_load(net_priv *c)
287 {
288     ENTER;
289     RET(0);
290 }
291 
292 #endif
293 
294 
295 static int
net_constructor(plugin_instance * p)296 net_constructor(plugin_instance *p)
297 {
298     net_priv *c;
299 
300     if (!(k = class_get("chart")))
301         RET(0);
302     if (!PLUGIN_CLASS(k)->constructor(p))
303         RET(0);
304     c = (net_priv *) p;
305 
306     c->iface = "eth0";
307     c->max_rx = 120;
308     c->max_tx = 12;
309     c->colors[0] = "violet";
310     c->colors[1] = "blue";
311     XCG(p->xc, "interface", &c->iface, str);
312     XCG(p->xc, "RxLimit", &c->max_rx, int);
313     XCG(p->xc, "TxLimit", &c->max_tx, int);
314     XCG(p->xc, "TxColor", &c->colors[0], str);
315     XCG(p->xc, "RxColor", &c->colors[1], str);
316 
317     c->max = c->max_rx + c->max_tx;
318     k->set_rows(&c->chart, 2, c->colors);
319     gtk_widget_set_tooltip_markup(((plugin_instance *)c)->pwid, "<b>Net</b>");
320     net_get_load(c);
321     c->timer = g_timeout_add(CHECK_PERIOD * 1000,
322         (GSourceFunc) net_get_load, (gpointer) c);
323     RET(1);
324 }
325 
326 
327 static void
net_destructor(plugin_instance * p)328 net_destructor(plugin_instance *p)
329 {
330     net_priv *c = (net_priv *) p;
331 
332     ENTER;
333     if (c->timer)
334         g_source_remove(c->timer);
335     PLUGIN_CLASS(k)->destructor(p);
336     class_put("chart");
337     RET();
338 }
339 
340 
341 static plugin_class class = {
342     .count       = 0,
343     .type        = "net",
344     .name        = "Net usage",
345     .version     = "1.0",
346     .description = "Display net usage",
347     .priv_size   = sizeof(net_priv),
348 
349     .constructor = net_constructor,
350     .destructor  = net_destructor,
351 };
352 static plugin_class *class_ptr = (plugin_class *) &class;
353