Merging upstream version 0.7.0 (Closes: #493243, #510888, #567617, #699414, #709777...
[debian/lxpanel.git] / plugins / netstat / devproc.c
1 /**
2 * Copyright (c) 2008 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 #include <string.h>
19 #include <glib.h>
20 #include <fcntl.h>
21 #include <sys/select.h>
22 #include <sys/socket.h>
23 #include <sys/ioctl.h>
24 #include <netinet/in.h>
25 #include <net/if.h>
26 #include <net/if_arp.h>
27 #include <arpa/inet.h>
28 #include <linux/sockios.h>
29 #include <linux/types.h>
30 #include <linux/ethtool.h>
31 #include <iwlib.h>
32 #include "nsconfig.h"
33 #include "netstat.h"
34 #include "statusicon.h"
35 #include "devproc.h"
36 #include "dbg.h"
37
38 /* network device list */
39 static void netproc_netdevlist_add(NETDEVLIST_PTR *netdev_list,
40 const char *ifname,
41 gulong recv_bytes,
42 gulong recv_packets,
43 gulong trans_bytes,
44 gulong trans_packets,
45 gboolean wireless)
46 {
47 NETDEVLIST_PTR new_dev;
48
49 new_dev = malloc(sizeof(NETDEVLIST));
50 new_dev->info.ifname = g_strdup(ifname);
51 new_dev->info.mac = NULL;
52 new_dev->info.ipaddr = NULL;
53 new_dev->info.dest = NULL;
54 new_dev->info.bcast = NULL;
55 new_dev->info.mask = NULL;
56 new_dev->info.alive = TRUE;
57 new_dev->info.enable = FALSE;
58 new_dev->info.updated = TRUE;
59 new_dev->info.plug = TRUE;
60 new_dev->info.connected = TRUE;
61 new_dev->info.wireless = wireless;
62 new_dev->info.status = NETDEV_STAT_NORMAL;
63 new_dev->info.recv_bytes = recv_bytes;
64 new_dev->info.recv_packets = recv_packets;
65 new_dev->info.trans_bytes = trans_bytes;
66 new_dev->info.trans_packets = trans_packets;
67 new_dev->info.status_icon = NULL;
68 new_dev->info.pg = NULL;
69 new_dev->prev = NULL;
70 new_dev->next = *netdev_list;
71 if (new_dev->next!=NULL) {
72 new_dev->next->prev = new_dev;
73 }
74 *netdev_list = new_dev;
75 }
76
77 static void netproc_netdevlist_destroy(NETDEVLIST_PTR netdev_list)
78 {
79 g_free(netdev_list->info.ifname);
80 g_free(netdev_list->info.mac);
81 g_free(netdev_list->info.ipaddr);
82 g_free(netdev_list->info.dest);
83 g_free(netdev_list->info.bcast);
84 g_free(netdev_list->info.mask);
85 statusicon_destroy(netdev_list->info.status_icon);
86 }
87
88 int netproc_netdevlist_clear(NETDEVLIST_PTR *netdev_list)
89 {
90 NETDEVLIST_PTR ptr;
91 NETDEVLIST_PTR ptr_del;
92
93 if (*netdev_list==NULL)
94 return 0;
95
96 ptr = *netdev_list;
97 while(ptr->next!=NULL) {
98 ptr_del = ptr;
99 ptr = ptr->next;
100 netproc_netdevlist_destroy(ptr_del);
101 free(ptr_del);
102 }
103
104 *netdev_list = NULL;
105
106 return 0;
107 }
108
109
110 static NETDEVLIST_PTR netproc_netdevlist_find(
111 NETDEVLIST_PTR netdev_list, const char *ifname)
112 {
113 NETDEVLIST_PTR ptr;
114
115 if (netdev_list==NULL)
116 return NULL;
117
118 ptr = netdev_list;
119 do {
120
121 if (strcmp(ptr->info.ifname, ifname)==0)
122 return ptr;
123
124 ptr = ptr->next;
125 } while(ptr!=NULL);
126
127 return NULL;
128 }
129
130 static char *netproc_parse_ifname(const char *buffer)
131 {
132 char *ptr;
133 if ((ptr = strchr(buffer, ':'))) {
134 *ptr++ = '\0';
135 }
136
137 return ptr;
138 }
139
140 static void netproc_parse_stats_header(char *buffer,
141 int *prx_idx,
142 int *ptx_idx,
143 int *brx_idx,
144 int *btx_idx)
145 {
146 char *p;
147 int i;
148
149 *prx_idx = *ptx_idx = -1;
150 *brx_idx = *btx_idx = -1;
151
152 p = strtok (buffer, "| \t\n");
153 p = strtok (NULL, "| \t\n");
154 for (i = 0; p; i++, p = strtok (NULL, "| \t\n")) {
155 if (!strcmp (p, "packets")) {
156 if (*prx_idx == -1)
157 *prx_idx = i;
158 else
159 *ptx_idx = i;
160 } else if (!strcmp (p, "bytes")) {
161 if (*brx_idx == -1)
162 *brx_idx = i;
163 else
164 *btx_idx = i;
165 }
166 }
167 }
168
169 static gboolean netproc_parse_status(char *buffer,
170 int prx_idx,
171 int ptx_idx,
172 gulong *in_packets,
173 gulong *out_packets,
174 int brx_idx,
175 int btx_idx,
176 gulong *in_bytes,
177 gulong *out_bytes)
178 {
179 char *ptr;
180 int i;
181 ptr = strtok(buffer, " \t\n");
182 for (i=0;ptr;i++, ptr = strtok(NULL, " \t\n")) {
183 if (i==prx_idx)
184 *in_packets = g_ascii_strtoull(ptr, NULL, 10);
185 else if (i==ptx_idx)
186 *out_packets = g_ascii_strtoull(ptr, NULL, 10);
187 else if (i==brx_idx)
188 *in_bytes = g_ascii_strtoull(ptr, NULL, 10);
189 else if (i==btx_idx)
190 *out_bytes = g_ascii_strtoull(ptr, NULL, 10);
191 }
192
193 if (i <= prx_idx || i <= ptx_idx || i <= brx_idx || i <=btx_idx)
194 return FALSE;
195
196 return TRUE;
197 }
198
199 FILE *netproc_open()
200 {
201 return fopen("/proc/net/dev", "r");
202 }
203
204 void netproc_close(FILE *fp)
205 {
206 fclose(fp);
207 }
208
209 int netproc_scandevice(int sockfd, int iwsockfd, FILE *fp, NETDEVLIST_PTR *netdev_list)
210 {
211 char buffer[512];
212 int count = 0;
213 int prx_idx, ptx_idx, brx_idx, btx_idx;
214 gulong in_packets, out_packets, in_bytes, out_bytes;
215 NETDEVLIST_PTR devptr = NULL;
216
217 /* interface information */
218 struct ifreq ifr;
219 struct ethtool_test edata;
220 iwstats iws;
221 char *status;
222 char *name;
223 struct iw_range iwrange;
224 int has_iwrange = 0;
225
226 status = fgets (buffer, sizeof(buffer), fp);
227 if (!status)
228 g_warning("netstat: netproc_scnadevice(): Error reading first line from stream!");
229 status = fgets (buffer, sizeof(buffer), fp);
230 if (!status)
231 g_warning("netstat: netproc_scnadevice(): Error reading second line from stream!");
232 netproc_parse_stats_header(buffer, &prx_idx, &ptx_idx, &brx_idx, &btx_idx);
233
234 while (fgets(buffer, sizeof(buffer), fp)) {
235 /* getting interface name */
236 name = buffer;
237 while (g_ascii_isspace(name[0])) {
238 name++;
239 }
240
241 /* reading packet infomation */
242 in_packets = out_packets = in_bytes = out_bytes = 0L;
243 status = netproc_parse_ifname(name);
244 netproc_parse_status(status, prx_idx, ptx_idx, &in_packets, &out_packets,
245 brx_idx, btx_idx, &in_bytes, &out_bytes);
246
247 /* check interface hw_type */
248 bzero(&ifr, sizeof(ifr));
249 strncpy(ifr.ifr_name, name, strlen(name));
250 ifr.ifr_name[strlen(name)+1] = '\0';
251 if (ioctl(sockfd, SIOCGIFHWADDR, &ifr)<0)
252 continue;
253
254 /* hw_types is not Ethernet and PPP */
255 if (ifr.ifr_hwaddr.sa_family!=ARPHRD_ETHER&&ifr.ifr_hwaddr.sa_family!=ARPHRD_PPP)
256 continue;
257
258 /* detecting new interface */
259 if ((devptr = netproc_netdevlist_find(*netdev_list, name))==NULL) {
260 /* check wireless device */
261 has_iwrange = (iw_get_range_info(iwsockfd, name, &iwrange)>=0);
262 if (!(has_iwrange) || (iwrange.we_version_compiled < 14))
263 netproc_netdevlist_add(netdev_list, name, in_bytes, in_packets, out_bytes, out_packets, FALSE);
264 else
265 netproc_netdevlist_add(netdev_list, name, in_bytes, in_packets, out_bytes, out_packets, TRUE);
266
267 devptr = netproc_netdevlist_find(*netdev_list, name);
268
269 /* MAC Address */
270 devptr->info.mac = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
271 ifr.ifr_hwaddr.sa_data[0] & 0377,
272 ifr.ifr_hwaddr.sa_data[1] & 0377,
273 ifr.ifr_hwaddr.sa_data[2] & 0377,
274 ifr.ifr_hwaddr.sa_data[3] & 0377,
275 ifr.ifr_hwaddr.sa_data[4] & 0377,
276 ifr.ifr_hwaddr.sa_data[5] & 0377);
277 } else {
278 /* Setting device status and update flags */
279 if (devptr->info.recv_packets!=in_packets&&devptr->info.trans_packets!=out_packets) {
280 if (devptr->info.status!=NETDEV_STAT_BOTHRS)
281 devptr->info.updated = TRUE;
282
283 devptr->info.status = NETDEV_STAT_BOTHRS;
284 } else if (devptr->info.recv_packets!=in_packets) {
285 if (devptr->info.status!=NETDEV_STAT_RECVDATA)
286 devptr->info.updated = TRUE;
287
288 devptr->info.status = NETDEV_STAT_RECVDATA;
289 } else if (devptr->info.trans_packets!=out_packets) {
290 if (devptr->info.status!=NETDEV_STAT_SENDDATA)
291 devptr->info.updated = TRUE;
292
293 devptr->info.status = NETDEV_STAT_SENDDATA;
294 } else {
295 if (devptr->info.status!=NETDEV_STAT_NORMAL)
296 devptr->info.updated = TRUE;
297
298 devptr->info.status = NETDEV_STAT_NORMAL;
299 }
300
301 /* Recording r/t information */
302 devptr->info.recv_bytes = in_bytes;
303 devptr->info.recv_packets = in_packets;
304 devptr->info.trans_bytes = out_bytes;
305 devptr->info.trans_packets = out_packets;
306
307 /* give device a life */
308 devptr->info.alive = TRUE;
309 }
310
311 /* Enable */
312 bzero(&ifr, sizeof(ifr));
313 strcpy(ifr.ifr_name, devptr->info.ifname);
314 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
315 if (ioctl(sockfd, SIOCGIFFLAGS, &ifr)>=0) {
316 devptr->info.flags = ifr.ifr_flags;
317 if (ifr.ifr_flags & IFF_UP) {
318 devptr->info.enable = TRUE;
319 devptr->info.updated = TRUE;
320 } else {
321 devptr->info.enable = FALSE;
322 devptr->info.updated = TRUE;
323 }
324
325 if (devptr->info.enable) {
326 /* Workaround for Atheros Cards */
327 if (strncmp(devptr->info.ifname, "ath", 3)==0)
328 wireless_refresh(iwsockfd, devptr->info.ifname);
329
330 /* plug */
331 bzero(&ifr, sizeof(ifr));
332 strcpy(ifr.ifr_name, devptr->info.ifname);
333 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
334
335 edata.cmd = 0x0000000a;
336 ifr.ifr_data = (caddr_t)&edata;
337 if (ioctl(sockfd, SIOCETHTOOL, &ifr)<0) {
338 /* using IFF_RUNNING instead due to system doesn't have ethtool or working in non-root */
339 if (devptr->info.flags & IFF_RUNNING) {
340 if (!devptr->info.plug) {
341 devptr->info.plug = TRUE;
342 devptr->info.updated = TRUE;
343 }
344 } else if (devptr->info.plug) {
345 devptr->info.plug = FALSE;
346 devptr->info.updated = TRUE;
347 }
348 } else {
349 if (edata.data) {
350 if (!devptr->info.plug) {
351 devptr->info.plug = TRUE;
352 devptr->info.updated = TRUE;
353 }
354 } else if (devptr->info.plug) {
355 devptr->info.plug = FALSE;
356 devptr->info.updated = TRUE;
357 }
358 }
359
360 /* get network information */
361 if (devptr->info.enable&&devptr->info.plug) {
362 if (devptr->info.flags & IFF_RUNNING) {
363 /* release old information */
364 g_free(devptr->info.ipaddr);
365 g_free(devptr->info.bcast);
366 g_free(devptr->info.mask);
367
368 /* IP Address */
369 bzero(&ifr, sizeof(ifr));
370 strcpy(ifr.ifr_name, devptr->info.ifname);
371 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
372 if (ioctl(sockfd, SIOCGIFADDR, &ifr)<0)
373 devptr->info.ipaddr = g_strdup("0.0.0.0");
374 else
375 devptr->info.ipaddr = g_strdup(inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
376
377 /* Point-to-Porint Address */
378 if (devptr->info.flags & IFF_POINTOPOINT) {
379 bzero(&ifr, sizeof(ifr));
380 strcpy(ifr.ifr_name, devptr->info.ifname);
381 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
382 if (ioctl(sockfd, SIOCGIFDSTADDR, &ifr)<0)
383 devptr->info.dest = NULL;
384 else
385 devptr->info.dest = g_strdup(inet_ntoa(((struct sockaddr_in*)&ifr.ifr_dstaddr)->sin_addr));
386 }
387
388 /* Broadcast */
389 if (devptr->info.flags & IFF_BROADCAST) {
390 bzero(&ifr, sizeof(ifr));
391 strcpy(ifr.ifr_name, devptr->info.ifname);
392 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
393 if (ioctl(sockfd, SIOCGIFBRDADDR, &ifr)<0)
394 devptr->info.bcast = NULL;
395 else
396 devptr->info.bcast = g_strdup(inet_ntoa(((struct sockaddr_in*)&ifr.ifr_broadaddr)->sin_addr));
397 }
398
399 /* Netmask */
400 bzero(&ifr, sizeof(ifr));
401 strcpy(ifr.ifr_name, devptr->info.ifname);
402 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
403 if (ioctl(sockfd, SIOCGIFNETMASK, &ifr)<0)
404 devptr->info.mask = NULL;
405 else
406 devptr->info.mask = g_strdup(inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
407
408 /* Wireless Information */
409 if (devptr->info.wireless) {
410 struct wireless_config wconfig;
411
412 /* get wireless config */
413 if (iw_get_basic_config(iwsockfd, devptr->info.ifname, &wconfig)>=0) {
414 /* Protocol */
415 devptr->info.protocol = g_strdup(wconfig.name);
416 /* ESSID */
417 devptr->info.essid = g_strdup(wconfig.essid);
418
419 /* Signal Quality */
420 iw_get_stats(iwsockfd, devptr->info.ifname, &iws, &iwrange, has_iwrange);
421 devptr->info.quality = rint((log (iws.qual.qual) / log (92)) * 100.0);
422 }
423 }
424
425 /* check problem connection */
426 if (strcmp(devptr->info.ipaddr, "0.0.0.0")==0) {
427 devptr->info.status = NETDEV_STAT_PROBLEM;
428 /* has connection problem */
429 if (devptr->info.connected) {
430 devptr->info.connected = FALSE;
431 devptr->info.updated = TRUE;
432 }
433 } else if (!devptr->info.connected) {
434 devptr->info.status = NETDEV_STAT_NORMAL;
435 devptr->info.connected = TRUE;
436 devptr->info.updated = TRUE;
437 }
438 } else {
439 /* has connection problem */
440 devptr->info.status = NETDEV_STAT_PROBLEM;
441 if (devptr->info.connected) {
442 devptr->info.connected = FALSE;
443 devptr->info.updated = TRUE;
444 }
445 }
446 }
447 }
448 }
449
450 devptr = NULL;
451 count++;
452 }
453
454 rewind(fp);
455 fflush(fp);
456
457 return count;
458 }
459
460 static void netproc_alive(NETDEVLIST_PTR netdev_list)
461 {
462 NETDEVLIST_PTR ptr;
463
464 if (netdev_list==NULL) {
465 return;
466 }
467
468 ptr = netdev_list;
469 do {
470 ptr->info.alive = FALSE;
471 ptr = ptr->next;
472 } while(ptr!=NULL);
473 }
474
475 void netproc_devicelist_clear(NETDEVLIST_PTR *netdev_list)
476 {
477 NETDEVLIST_PTR ptr;
478 NETDEVLIST_PTR prev_ptr;
479 NETDEVLIST_PTR del_ptr;
480
481 if (*netdev_list==NULL) {
482 return;
483 }
484
485 prev_ptr = NULL;
486 ptr = *netdev_list;
487 do {
488 if (!ptr->info.alive) { /* if device was removed */
489 if (prev_ptr!=NULL) {
490 ptr->prev->next = ptr->next;
491 ptr->next->prev = ptr->prev;
492 } else {
493 ptr->next->prev = NULL;
494 *netdev_list = ptr->next;
495 }
496
497 del_ptr = ptr;
498 ptr = ptr->next;
499 netproc_netdevlist_destroy(del_ptr);
500 g_free(del_ptr);
501 } else {
502 prev_ptr = ptr;
503 ptr = ptr->next;
504 }
505 } while(ptr!=NULL);
506 }
507
508 void netproc_listener(FNETD *fnetd)
509 {
510 if (fnetd->sockfd) {
511 netproc_alive(fnetd->netdevlist);
512 fnetd->netdev_fp = netproc_open();
513 netproc_scandevice(fnetd->sockfd, fnetd->iwsockfd, fnetd->netdev_fp, &fnetd->netdevlist);
514 netproc_close(fnetd->netdev_fp);
515 }
516 }
517
518 #ifdef DEBUG
519 void netproc_print(NETDEVLIST_PTR netdev_list)
520 {
521 NETDEVLIST_PTR ptr;
522
523 if (netdev_list==NULL) {
524 return;
525 }
526
527 ptr = netdev_list;
528 do {
529 printf("%s: %d\n", ptr->info.ifname, ptr->info.status);
530 ptr = ptr->next;
531 } while(ptr!=NULL);
532 }
533 #endif