Merging upstream version 0.7.0 (Closes: #493243, #510888, #567617, #699414, #709777...
[debian/lxpanel.git] / plugins / netstatus / netstatus-sysdeps.c
1 /*
2 * Copyright (C) 2003 Sun Microsystems, Inc.
3 * Copyright (C) 2004 Red Hat Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 * 02111-1307, USA.
19 *
20 * Authors:
21 * Erwann Chenede <erwann.chenede@sun.com>
22 * Mark McLoughlin <mark@skynet.ie>
23 * Joe Marcus Clarke <marcus@freebsd.org>
24 */
25
26 #include <config.h>
27
28 #include "netstatus-sysdeps.h"
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <math.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <glib.h>
36 #include <glib/gi18n.h>
37
38 #ifdef __FreeBSD__
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/ioctl.h>
42 #include <net/if.h>
43 #include <net/if_var.h>
44 #include <dev/an/if_aironet_ieee.h>
45 #include <dev/wi/if_wavelan_ieee.h>
46 #endif
47
48 static inline gboolean
49 parse_stats (char *buf,
50 int prx_idx,
51 int ptx_idx,
52 gulong *in_packets,
53 gulong *out_packets,
54 int brx_idx,
55 int btx_idx,
56 gulong *in_bytes,
57 gulong *out_bytes)
58 {
59 char *p;
60 int i;
61
62 p = strtok (buf, " \t\n");
63 for (i = 0; p; i++, p = strtok (NULL, " \t\n"))
64 {
65 if (i == prx_idx)
66 *in_packets = g_ascii_strtoull (p, NULL, 10);
67 if (i == ptx_idx)
68 *out_packets = g_ascii_strtoull (p, NULL, 10);
69 if (i == brx_idx)
70 *in_bytes = g_ascii_strtoull (p, NULL, 10);
71 if (i == btx_idx)
72 *out_bytes = g_ascii_strtoull (p, NULL, 10);
73 }
74
75 if (i <= prx_idx || i <= ptx_idx || i <= brx_idx || i <=btx_idx)
76 return FALSE;
77
78 return TRUE;
79 }
80
81 #if !defined (__FreeBSD__)
82
83 static inline char *
84 parse_iface_name (const char *buf)
85 {
86 char *p1;
87
88 if ((p1 = strchr (buf, ':')))
89 {
90 char *p2;
91
92 p2 = strchr (p1, ':');
93 if (p2)
94 *p2++ = '\0';
95 else
96 *p1++ = '\0';
97
98 return p2 ? p2 : p1;
99 }
100 else if ((p1 = strchr (buf, ' ')))
101 {
102 *p1++ = '\0';
103 return p1;
104 }
105
106 return NULL;
107 }
108
109 static inline void
110 parse_stats_header (char *buf,
111 int *prx_idx,
112 int *ptx_idx,
113 int *brx_idx,
114 int *btx_idx)
115 {
116 char *p;
117 int i;
118
119 *prx_idx = *ptx_idx = -1;
120 *brx_idx = *btx_idx = -1;
121
122 p = strtok (buf, "| \t\n");
123 p = strtok (NULL, "| \t\n"); /* Skip the first one */
124 for (i = 0; p; i++, p = strtok (NULL, "| \t\n"))
125 {
126 if (!strcmp (p, "packets"))
127 {
128 if (*prx_idx == -1)
129 *prx_idx = i;
130 else
131 *ptx_idx = i;
132 }
133 else if (!strcmp (p, "bytes"))
134 {
135 if (*brx_idx == -1)
136 *brx_idx = i;
137 else
138 *btx_idx = i;
139 }
140 }
141 }
142
143 static inline FILE *
144 get_proc_net_dev_fh (void)
145 {
146 static FILE *retval = NULL;
147
148 if (retval != NULL)
149 return retval;
150
151 return retval = fopen ("/proc/net/dev", "r");
152 }
153
154 char *
155 netstatus_sysdeps_read_iface_statistics (const char *iface,
156 gulong *in_packets,
157 gulong *out_packets,
158 gulong *in_bytes,
159 gulong *out_bytes)
160 {
161 FILE *fh;
162 char buf [512];
163 int prx_idx, ptx_idx;
164 int brx_idx, btx_idx;
165 char *error_message = NULL;
166
167 g_return_val_if_fail (iface != NULL, NULL);
168 g_return_val_if_fail (in_packets != NULL, NULL);
169 g_return_val_if_fail (out_packets != NULL, NULL);
170 g_return_val_if_fail (in_bytes != NULL, NULL);
171 g_return_val_if_fail (out_bytes != NULL, NULL);
172
173 *in_packets = -1;
174 *out_packets = -1;
175 *in_bytes = -1;
176 *out_bytes = -1;
177
178 fh = get_proc_net_dev_fh ();
179 if (!fh)
180 return g_strdup_printf (_("Cannot open /proc/net/dev: %s"),
181 g_strerror (errno));
182
183 if (fgets (buf, sizeof (buf), fh) == NULL ||
184 fgets (buf, sizeof (buf), fh) == NULL)
185 return g_strdup (_("Could not parse /proc/net/dev. No data."));
186
187 parse_stats_header (buf, &prx_idx, &ptx_idx, &brx_idx, &btx_idx);
188 if (prx_idx == -1 || ptx_idx == -1 ||
189 brx_idx == -1 || btx_idx == -1)
190 return g_strdup (_("Could not parse /proc/net/dev. Unknown format."));
191
192 while (fgets (buf, sizeof (buf), fh))
193 {
194 char *stats;
195 char *name;
196
197 name = buf;
198 while (g_ascii_isspace (name [0]))
199 name++;
200
201 stats = parse_iface_name (name);
202 if (!stats)
203 {
204 if (!error_message)
205 error_message = g_strdup_printf (_("Could not parse interface name from '%s'"), buf);
206 continue;
207 }
208
209 if (strcmp (name, iface) != 0)
210 continue;
211
212 if (!parse_stats (stats,
213 prx_idx, ptx_idx, in_packets, out_packets,
214 brx_idx, btx_idx, in_bytes, out_bytes))
215 {
216 if (error_message)
217 g_free (error_message);
218 error_message = g_strdup_printf (_("Could not parse interface statistics from '%s'. "
219 "prx_idx = %d; ptx_idx = %d; brx_idx = %d; btx_idx = %d;"),
220 buf, prx_idx, ptx_idx, brx_idx, btx_idx);
221 continue;
222 }
223
224 break;
225 }
226
227 if ((*in_packets == (gulong) -1 || *out_packets == (gulong) -1 || *in_bytes == (gulong) -1 || *out_bytes == (gulong) -1) && !error_message)
228 error_message = g_strdup_printf ("Could not find information on interface '%s' in /proc/net/dev", iface);
229
230 rewind (fh);
231 fflush (fh);
232
233 return error_message;
234 }
235
236 static inline gboolean
237 parse_wireless (char *buf,
238 int link_idx,
239 int *link)
240 {
241 char *p;
242 int i;
243
244 p = strtok (buf, " \t\n");
245 for (i = 0; p; i++, p = strtok (NULL, " \t\n"))
246 {
247 if (i == link_idx)
248 *link = g_ascii_strtoull (p, NULL, 10);
249 }
250
251 if (i <= link_idx)
252 return FALSE;
253
254 return TRUE;
255 }
256
257 static inline int
258 parse_wireless_header (char *buf)
259 {
260 char *p;
261 int i;
262
263 p = strtok (buf, "| \t\n");
264 p = strtok (NULL, "| \t\n"); /* Skip the first one */
265 for (i = 0; p; i++, p = strtok (NULL, "| \t\n"))
266 {
267 if (!strcmp (p, "link"))
268 {
269 return i;
270 }
271 }
272
273 return -1;
274 }
275
276 static inline FILE *
277 get_proc_net_wireless_fh (void)
278 {
279 static FILE *retval = NULL;
280
281 if (retval != NULL)
282 return retval;
283
284 return retval = fopen ("/proc/net/wireless", "r");
285 }
286
287 char *
288 netstatus_sysdeps_read_iface_wireless_details (const char *iface,
289 gboolean *is_wireless,
290 int *signal_strength)
291 {
292 FILE *fh;
293 char buf [512];
294 int link_idx;
295 char *error_message = NULL;
296
297 g_return_val_if_fail (iface != NULL, NULL);
298 g_return_val_if_fail (is_wireless != NULL, NULL);
299 g_return_val_if_fail (signal_strength != NULL, NULL);
300
301 if (is_wireless)
302 *is_wireless = FALSE;
303 if (signal_strength)
304 *signal_strength = 0;
305
306 fh = get_proc_net_wireless_fh ();
307 if (!fh)
308 return NULL;
309
310 if (fgets (buf, sizeof (buf), fh) == NULL ||
311 fgets (buf, sizeof (buf), fh) == NULL)
312 return g_strdup (_("Could not parse /proc/net/wireless. No data."));
313
314 link_idx = parse_wireless_header (buf);
315 if (link_idx == -1)
316 return g_strdup (_("Could not parse /proc/net/wireless. Unknown format."));
317
318 while (fgets (buf, sizeof (buf), fh))
319 {
320 char *details;
321 char *name;
322 int link = 0;
323
324 name = buf;
325 while (g_ascii_isspace (name [0]))
326 name++;
327
328 details = parse_iface_name (name);
329 if (!details)
330 {
331 if (!error_message)
332 error_message = g_strdup_printf (_("Could not parse interface name from '%s'"), buf);
333 continue;
334 }
335
336 if (strcmp (name, iface) != 0)
337 continue;
338
339 if (!parse_wireless (details, link_idx, &link))
340 {
341 if (error_message)
342 g_free (error_message);
343 error_message = g_strdup_printf (_("Could not parse wireless details from '%s'. link_idx = %d;"),
344 buf, link_idx);
345 continue;
346 }
347
348 /* Stolen from the wireless applet */
349 *signal_strength = (int) rint ((log (link) / log (92)) * 100.0);
350 *signal_strength = CLAMP (*signal_strength, 0, 100);
351 *is_wireless = TRUE;
352
353 break;
354 }
355
356 rewind (fh);
357 fflush (fh);
358
359 return error_message;
360 }
361
362 #else /* defined(__FreeBSD__) */
363
364 static inline void
365 parse_header (char *buf,
366 int *prx_idx,
367 int *ptx_idx,
368 int *brx_idx,
369 int *btx_idx)
370 {
371 char *p;
372 int i;
373
374 *prx_idx = *ptx_idx = -1;
375 *brx_idx = *btx_idx = -1;
376
377 p = strtok (buf, " \n\t");
378 for (i = 0; p; i++, p = strtok (NULL, " \t\n"))
379 {
380 if (!strcmp (p, "Ipkts"))
381 {
382 *prx_idx = i;
383 }
384 else if (!strcmp (p, "Ibytes"))
385 {
386 *brx_idx = i;
387 }
388 else if (!strcmp (p, "Opkts"))
389 {
390 *ptx_idx = i;
391 }
392 else if (!strcmp (p, "Obytes"))
393 {
394 *btx_idx = i;
395 }
396 }
397 }
398
399 static inline gboolean
400 wireless_getval (const char *iface,
401 gpointer req,
402 unsigned long req_type,
403 char **error)
404 {
405 struct ifreq ifr;
406 int s;
407
408 memset (&ifr, 0, sizeof (ifr));
409
410 strlcpy (ifr.ifr_name, iface, sizeof (ifr.ifr_name));
411 ifr.ifr_data = (caddr_t) req;
412
413 s = socket (AF_INET, SOCK_DGRAM, 0);
414 if (s == -1)
415 {
416 *error = g_strdup_printf (_("Could not connect to interface, '%s'"), iface);
417 return FALSE;
418 }
419
420 if (ioctl (s, req_type, &ifr) == -1)
421 {
422 *error = g_strdup_printf (_("Could not send ioctl to interface, '%s'"), iface);
423 close (s);
424 return FALSE;
425 }
426
427 close (s);
428 return TRUE;
429 }
430
431 static inline char *
432 get_an_data (const char *iface,
433 int *signal_strength)
434 {
435 #ifdef AN_RID_RSSI_MAP
436 struct an_ltv_rssi_map an_rssimap;
437 #endif
438 struct an_req areq;
439 struct an_ltv_status *sts;
440 int level;
441 char *error = NULL;
442 gboolean rssimap_valid = FALSE;
443
444 #ifdef AN_RID_RSSI_MAP
445 an_rssimap.an_len = sizeof (an_rssimap);
446 an_rssimap.an_type = AN_RID_RSSI_MAP;
447 rssimap_valid = wireless_getval (iface, (gpointer) &an_rssimap, SIOCGAIRONET, &error);
448 #endif
449
450 areq.an_len = sizeof (areq);
451 areq.an_type = AN_RID_STATUS;
452
453 if (!wireless_getval (iface, (gpointer) &areq, SIOCGAIRONET, &error))
454 return error;
455
456 sts = (struct an_ltv_status *) &areq;
457
458 #ifdef AN_RID_RSSI_MAP
459 if (rssimap_valid)
460 level = (int) (an_rssimap.an_entries[sts->an_normalized_strength].an_rss_pct);
461 else
462 level = (int) (sts->an_normalized_strength);
463 #else
464 level = (int) (sts->an_normalized_rssi);
465 #endif
466
467 memcpy (signal_strength, &level, sizeof (signal_strength));
468
469 return error;
470 }
471
472 static inline char *
473 get_wi_data (const char *iface,
474 int *signal_strength)
475 {
476 struct wi_req wreq;
477 int level;
478 char *error = NULL;
479
480 memset (&wreq, 0, sizeof (wreq));
481
482 wreq.wi_len = WI_MAX_DATALEN;
483 wreq.wi_type = WI_RID_COMMS_QUALITY;
484
485 if (!wireless_getval (iface, &wreq, SIOCGWAVELAN, &error))
486 return error;
487
488 level = (int) wreq.wi_val[1];
489
490 #ifdef WI_RID_READ_APS
491 if (signal_strength <= 0)
492 {
493 /* we fail to get signal strength by usual means, try another way */
494 static time_t last_scan;
495 static long int cached;
496 time_t now;
497
498 now = time (NULL);
499
500 /* XXX: this is long operation, and we will scan station not often then one in 5 secs */
501 if (now > last_scan + 5)
502 {
503 struct wi_apinfo *w;
504 int nstations;
505
506 bzero (&wreq, sizeof (wreq));
507 wreq.wi_len = WI_MAX_DATALEN;
508 wreq.wi_type = WI_RID_READ_APS;
509 if (!wireless_getval (iface, &wreq, SIOCGWAVELAN, &error))
510 return error;
511 nstations = *(int *) wreq.wi_val;
512 if (nstations > 0)
513 {
514 w = (struct wi_apinfo *)(((char *) &wreq.wi_val) + sizeof (int));
515 signal_strength = (long int) w->signal;
516 }
517
518 cached = signal_strength;
519 last_scan = now;
520 }
521 else
522 {
523 signal_strength = cached;
524 }
525 }
526 #endif
527
528 memcpy (signal_strength, &level, sizeof (signal_strength));
529
530 return error;
531 }
532
533 char *
534 netstatus_sysdeps_read_iface_wireless_details (const char *iface,
535 gboolean *is_wireless,
536 int *signal_strength)
537 {
538 char *error_message = NULL;
539
540 g_return_val_if_fail (iface != NULL, NULL);
541 g_return_val_if_fail (is_wireless != NULL, NULL);
542 g_return_val_if_fail (signal_strength != NULL, NULL);
543
544 if (is_wireless)
545 *is_wireless = FALSE;
546 if (signal_strength)
547 *signal_strength = 0;
548
549 if (g_strncasecmp (iface, "an", 2) &&
550 g_strncasecmp (iface, "wi", 2) &&
551 g_strncasecmp (iface, "ath", 3) &&
552 g_strncasecmp (iface, "ndis", 4) &&
553 g_strncasecmp (iface, "ipw", 3) &&
554 g_strncasecmp (iface, "iwi", 3) &&
555 g_strncasecmp (iface, "acx", 3))
556 return error_message;
557
558 if (g_strncasecmp (iface, "an", 2) == 0)
559 {
560 error_message = get_an_data (iface, signal_strength);
561 *is_wireless = TRUE;
562 }
563 else
564 {
565 error_message = get_wi_data (iface, signal_strength);
566 *is_wireless = TRUE;
567 }
568
569 return error_message;
570 }
571
572 char *
573 netstatus_sysdeps_read_iface_statistics (const char *iface,
574 gulong *in_packets,
575 gulong *out_packets,
576 gulong *in_bytes,
577 gulong *out_bytes)
578 {
579 GError *error;
580 char *command_line;
581 char **argv;
582 char *error_message = NULL;
583 int pipe_out;
584
585 g_return_val_if_fail (iface != NULL, NULL);
586 g_return_val_if_fail (in_packets != NULL, NULL);
587 g_return_val_if_fail (out_packets != NULL, NULL);
588 g_return_val_if_fail (in_bytes != NULL, NULL);
589 g_return_val_if_fail (out_bytes != NULL, NULL);
590
591 *in_packets = -1;
592 *out_packets = -1;
593 *in_bytes = -1;
594 *out_bytes = -1;
595
596 error = NULL;
597 command_line = g_strdup_printf ("/usr/bin/netstat -n -I %s -b -f inet", iface);
598 if (!g_shell_parse_argv (command_line, NULL, &argv, &error))
599 {
600 error_message = g_strdup_printf (_("Could not parse command line '%s': %s"),
601 command_line,
602 error->message);
603 g_error_free (error);
604 g_free (command_line);
605
606 return error_message;
607 }
608 g_free (command_line);
609
610 error = NULL;
611 if (g_spawn_async_with_pipes (NULL,
612 argv,
613 NULL,
614 0,
615 NULL,
616 NULL,
617 NULL,
618 NULL,
619 &pipe_out,
620 NULL,
621 &error))
622 {
623 GIOChannel *channel;
624 char *buf;
625 int prx_idx, ptx_idx;
626 int brx_idx, btx_idx;
627
628 channel = g_io_channel_unix_new (pipe_out);
629
630 g_io_channel_read_line (channel, &buf, NULL, NULL, NULL);
631 parse_header (buf, &prx_idx, &ptx_idx, &brx_idx, &btx_idx);
632 g_free (buf);
633
634 if (prx_idx == -1 || ptx_idx == -1 ||
635 brx_idx == -1 || btx_idx == -1)
636 {
637 error_message = g_strdup (_("Could not parse 'netstat' output. Unknown format"));
638 goto error_shutdown;
639 }
640
641 g_io_channel_read_line (channel, &buf, NULL, NULL, NULL);
642
643 if (!parse_stats (buf,
644 prx_idx, ptx_idx, in_packets, out_packets,
645 brx_idx, btx_idx, in_bytes, out_bytes))
646 {
647 error_message = g_strdup_printf (_("Could not parse interface statistics from '%s'. "
648 "prx_idx = %d; ptx_idx = %d; brx_idx = %d; btx_idx = %d;"),
649 buf, prx_idx, ptx_idx, brx_idx, btx_idx);
650 }
651 else if (*in_packets == -1 || *out_packets == -1 || *in_bytes == -1 || *out_bytes == -1)
652 {
653 error_message = g_strdup_printf ("Could not obtain information on interface '%s' from netstat",
654 iface);
655 }
656
657 g_free (buf);
658
659 error_shutdown:
660 g_io_channel_unref (channel);
661 close (pipe_out);
662 }
663 else
664 {
665 error_message = g_strdup_printf ("Error running /usr/bin/netstat for '%s': %s",
666 iface, error->message);
667 g_error_free (error);
668 }
669
670 g_strfreev (argv);
671
672 return error_message;
673 }
674
675 #endif /* !defined(__FreeBSD__) */