Enabling multithreaded compilation.
[debian/lxpanel.git] / src / 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 ERR("netstat: netproc_scnadevice(): Error reading first line from stream!\n");
229 status = fgets (buffer, sizeof(buffer), fp);
230 if (!status)
231 ERR("netstat: netproc_scnadevice(): Error reading second line from stream!\n");
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 status = netproc_parse_ifname(name);
243 netproc_parse_status(status, prx_idx, ptx_idx, &in_packets, &out_packets,
244 brx_idx, btx_idx, &in_bytes, &out_bytes);
245
246 /* check interface hw_type */
247 bzero(&ifr, sizeof(ifr));
248 strncpy(ifr.ifr_name, name, strlen(name));
249 ifr.ifr_name[strlen(name)+1] = '\0';
250 if (ioctl(sockfd, SIOCGIFHWADDR, &ifr)<0)
251 continue;
252
253 /* hw_types is not Ethernet and PPP */
254 if (ifr.ifr_hwaddr.sa_family!=ARPHRD_ETHER&&ifr.ifr_hwaddr.sa_family!=ARPHRD_PPP)
255 continue;
256
257 /* detecting new interface */
258 if ((devptr = netproc_netdevlist_find(*netdev_list, name))==NULL) {
259 /* check wireless device */
260 has_iwrange = (iw_get_range_info(iwsockfd, name, &iwrange)>=0);
261 if (!(has_iwrange) || (iwrange.we_version_compiled < 14))
262 netproc_netdevlist_add(netdev_list, name, in_bytes, in_packets, out_bytes, out_packets, FALSE);
263 else
264 netproc_netdevlist_add(netdev_list, name, in_bytes, in_packets, out_bytes, out_packets, TRUE);
265
266 devptr = netproc_netdevlist_find(*netdev_list, name);
267
268 /* MAC Address */
269 devptr->info.mac = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
270 ifr.ifr_hwaddr.sa_data[0] & 0377,
271 ifr.ifr_hwaddr.sa_data[1] & 0377,
272 ifr.ifr_hwaddr.sa_data[2] & 0377,
273 ifr.ifr_hwaddr.sa_data[3] & 0377,
274 ifr.ifr_hwaddr.sa_data[4] & 0377,
275 ifr.ifr_hwaddr.sa_data[5] & 0377);
276 } else {
277 /* Setting device status and update flags */
278 if (devptr->info.recv_packets!=in_packets&&devptr->info.trans_packets!=out_packets) {
279 if (devptr->info.status!=NETDEV_STAT_BOTHRS)
280 devptr->info.updated = TRUE;
281
282 devptr->info.status = NETDEV_STAT_BOTHRS;
283 } else if (devptr->info.recv_packets!=in_packets) {
284 if (devptr->info.status!=NETDEV_STAT_RECVDATA)
285 devptr->info.updated = TRUE;
286
287 devptr->info.status = NETDEV_STAT_RECVDATA;
288 } else if (devptr->info.trans_packets!=out_packets) {
289 if (devptr->info.status!=NETDEV_STAT_SENDDATA)
290 devptr->info.updated = TRUE;
291
292 devptr->info.status = NETDEV_STAT_SENDDATA;
293 } else {
294 if (devptr->info.status!=NETDEV_STAT_NORMAL)
295 devptr->info.updated = TRUE;
296
297 devptr->info.status = NETDEV_STAT_NORMAL;
298 }
299
300 /* Recording r/t information */
301 devptr->info.recv_bytes = in_bytes;
302 devptr->info.recv_packets = in_packets;
303 devptr->info.trans_bytes = out_bytes;
304 devptr->info.trans_packets = out_packets;
305
306 /* give device a life */
307 devptr->info.alive = TRUE;
308 }
309
310 /* Enable */
311 bzero(&ifr, sizeof(ifr));
312 strcpy(ifr.ifr_name, devptr->info.ifname);
313 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
314 if (ioctl(sockfd, SIOCGIFFLAGS, &ifr)>=0) {
315 devptr->info.flags = ifr.ifr_flags;
316 if (ifr.ifr_flags & IFF_UP) {
317 devptr->info.enable = TRUE;
318 devptr->info.updated = TRUE;
319 } else {
320 devptr->info.enable = FALSE;
321 devptr->info.updated = TRUE;
322 }
323
324 if (devptr->info.enable) {
325 /* Workaround for Atheros Cards */
326 if (strncmp(devptr->info.ifname, "ath", 3)==0)
327 wireless_refresh(iwsockfd, devptr->info.ifname);
328
329 /* plug */
330 bzero(&ifr, sizeof(ifr));
331 strcpy(ifr.ifr_name, devptr->info.ifname);
332 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
333
334 edata.cmd = 0x0000000a;
335 ifr.ifr_data = (caddr_t)&edata;
336 if (ioctl(sockfd, SIOCETHTOOL, &ifr)<0) {
337 /* using IFF_RUNNING instead due to system doesn't have ethtool or working in non-root */
338 if (devptr->info.flags & IFF_RUNNING) {
339 if (!devptr->info.plug) {
340 devptr->info.plug = TRUE;
341 devptr->info.updated = TRUE;
342 }
343 } else if (devptr->info.plug) {
344 devptr->info.plug = FALSE;
345 devptr->info.updated = TRUE;
346 }
347 } else {
348 if (edata.data) {
349 if (!devptr->info.plug) {
350 devptr->info.plug = TRUE;
351 devptr->info.updated = TRUE;
352 }
353 } else if (devptr->info.plug) {
354 devptr->info.plug = FALSE;
355 devptr->info.updated = TRUE;
356 }
357 }
358
359 /* get network information */
360 if (devptr->info.enable&&devptr->info.plug) {
361 if (devptr->info.flags & IFF_RUNNING) {
362 /* release old information */
363 g_free(devptr->info.ipaddr);
364 g_free(devptr->info.bcast);
365 g_free(devptr->info.mask);
366
367 /* IP Address */
368 bzero(&ifr, sizeof(ifr));
369 strcpy(ifr.ifr_name, devptr->info.ifname);
370 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
371 if (ioctl(sockfd, SIOCGIFADDR, &ifr)<0)
372 devptr->info.ipaddr = g_strdup("0.0.0.0");
373 else
374 devptr->info.ipaddr = g_strdup(inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
375
376 /* Point-to-Porint Address */
377 if (devptr->info.flags & IFF_POINTOPOINT) {
378 bzero(&ifr, sizeof(ifr));
379 strcpy(ifr.ifr_name, devptr->info.ifname);
380 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
381 if (ioctl(sockfd, SIOCGIFDSTADDR, &ifr)<0)
382 devptr->info.dest = NULL;
383 else
384 devptr->info.dest = g_strdup(inet_ntoa(((struct sockaddr_in*)&ifr.ifr_dstaddr)->sin_addr));
385 }
386
387 /* Broadcast */
388 if (devptr->info.flags & IFF_BROADCAST) {
389 bzero(&ifr, sizeof(ifr));
390 strcpy(ifr.ifr_name, devptr->info.ifname);
391 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
392 if (ioctl(sockfd, SIOCGIFBRDADDR, &ifr)<0)
393 devptr->info.bcast = NULL;
394 else
395 devptr->info.bcast = g_strdup(inet_ntoa(((struct sockaddr_in*)&ifr.ifr_broadaddr)->sin_addr));
396 }
397
398 /* Netmask */
399 bzero(&ifr, sizeof(ifr));
400 strcpy(ifr.ifr_name, devptr->info.ifname);
401 ifr.ifr_name[IF_NAMESIZE - 1] = '\0';
402 if (ioctl(sockfd, SIOCGIFNETMASK, &ifr)<0)
403 devptr->info.mask = NULL;
404 else
405 devptr->info.mask = g_strdup(inet_ntoa(((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr));
406
407 /* Wireless Information */
408 if (devptr->info.wireless) {
409 struct wireless_config wconfig;
410
411 /* get wireless config */
412 if (iw_get_basic_config(iwsockfd, devptr->info.ifname, &wconfig)>=0) {
413 /* Protocol */
414 devptr->info.protocol = g_strdup(wconfig.name);
415 /* ESSID */
416 devptr->info.essid = g_strdup(wconfig.essid);
417
418 /* Signal Quality */
419 iw_get_stats(iwsockfd, devptr->info.ifname, &iws, &iwrange, has_iwrange);
420 devptr->info.quality = rint((log (iws.qual.qual) / log (92)) * 100.0);
421 }
422 }
423
424 /* check problem connection */
425 if (strcmp(devptr->info.ipaddr, "0.0.0.0")==0) {
426 devptr->info.status = NETDEV_STAT_PROBLEM;
427 /* has connection problem */
428 if (devptr->info.connected) {
429 devptr->info.connected = FALSE;
430 devptr->info.updated = TRUE;
431 }
432 } else if (!devptr->info.connected) {
433 devptr->info.status = NETDEV_STAT_NORMAL;
434 devptr->info.connected = TRUE;
435 devptr->info.updated = TRUE;
436 }
437 } else {
438 /* has connection problem */
439 devptr->info.status = NETDEV_STAT_PROBLEM;
440 if (devptr->info.connected) {
441 devptr->info.connected = FALSE;
442 devptr->info.updated = TRUE;
443 }
444 }
445 }
446 }
447 }
448
449 devptr = NULL;
450 count++;
451 }
452
453 rewind(fp);
454 fflush(fp);
455
456 return count;
457 }
458
459 static void netproc_alive(NETDEVLIST_PTR netdev_list)
460 {
461 NETDEVLIST_PTR ptr;
462
463 if (netdev_list==NULL) {
464 return;
465 }
466
467 ptr = netdev_list;
468 do {
469 ptr->info.alive = FALSE;
470 ptr = ptr->next;
471 } while(ptr!=NULL);
472 }
473
474 void netproc_devicelist_clear(NETDEVLIST_PTR *netdev_list)
475 {
476 NETDEVLIST_PTR ptr;
477 NETDEVLIST_PTR prev_ptr;
478 NETDEVLIST_PTR del_ptr;
479
480 if (*netdev_list==NULL) {
481 return;
482 }
483
484 prev_ptr = NULL;
485 ptr = *netdev_list;
486 do {
487 if (!ptr->info.alive) { /* if device was removed */
488 if (prev_ptr!=NULL) {
489 ptr->prev->next = ptr->next;
490 ptr->next->prev = ptr->prev;
491 } else {
492 ptr->next->prev = NULL;
493 *netdev_list = ptr->next;
494 }
495
496 del_ptr = ptr;
497 ptr = ptr->next;
498 netproc_netdevlist_destroy(del_ptr);
499 g_free(del_ptr);
500 } else {
501 prev_ptr = ptr;
502 ptr = ptr->next;
503 }
504 } while(ptr!=NULL);
505 }
506
507 void netproc_listener(FNETD *fnetd)
508 {
509 if (fnetd->sockfd) {
510 netproc_alive(fnetd->netdevlist);
511 fnetd->netdev_fp = netproc_open();
512 netproc_scandevice(fnetd->sockfd, fnetd->iwsockfd, fnetd->netdev_fp, &fnetd->netdevlist);
513 netproc_close(fnetd->netdev_fp);
514 }
515 }
516
517 #ifdef DEBUG
518 void netproc_print(NETDEVLIST_PTR netdev_list)
519 {
520 NETDEVLIST_PTR ptr;
521
522 if (netdev_list==NULL) {
523 return;
524 }
525
526 ptr = netdev_list;
527 do {
528 printf("%s: %d\n", ptr->info.ifname, ptr->info.status);
529 ptr = ptr->next;
530 } while(ptr!=NULL);
531 }
532 #endif