Enabling multithreaded compilation.
[debian/lxpanel.git] / src / plugins / netstatus / netstatus-sysdeps.c
CommitLineData
6cc5e1a6
DB
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
48static inline gboolean
49parse_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
83static inline char *
84parse_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
109static inline void
110parse_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
143static inline FILE *
144get_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
154char *
155netstatus_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 fgets (buf, sizeof (buf), fh);
184 fgets (buf, sizeof (buf), fh);
185
186 parse_stats_header (buf, &prx_idx, &ptx_idx, &brx_idx, &btx_idx);
187 if (prx_idx == -1 || ptx_idx == -1 ||
188 brx_idx == -1 || btx_idx == -1)
189 return g_strdup (_("Could not parse /proc/net/dev. Unknown format."));
190
191 while (fgets (buf, sizeof (buf), fh))
192 {
193 char *stats;
194 char *name;
195
196 name = buf;
197 while (g_ascii_isspace (name [0]))
198 name++;
199
200 stats = parse_iface_name (name);
201 if (!stats)
202 {
203 if (!error_message)
204 error_message = g_strdup_printf (_("Could not parse interface name from '%s'"), buf);
205 continue;
206 }
207
208 if (strcmp (name, iface) != 0)
209 continue;
210
211 if (!parse_stats (stats,
212 prx_idx, ptx_idx, in_packets, out_packets,
213 brx_idx, btx_idx, in_bytes, out_bytes))
214 {
215 if (error_message)
216 g_free (error_message);
217 error_message = g_strdup_printf (_("Could not parse interface statistics from '%s'. "
218 "prx_idx = %d; ptx_idx = %d; brx_idx = %d; btx_idx = %d;"),
219 buf, prx_idx, ptx_idx, brx_idx, btx_idx);
220 continue;
221 }
222
223 break;
224 }
225
10862fa6 226 if ((*in_packets == (gulong) -1 || *out_packets == (gulong) -1 || *in_bytes == (gulong) -1 || *out_bytes == (gulong) -1) && !error_message)
6cc5e1a6
DB
227 error_message = g_strdup_printf ("Could not find information on interface '%s' in /proc/net/dev", iface);
228
229 rewind (fh);
230 fflush (fh);
231
232 return error_message;
233}
234
235static inline gboolean
236parse_wireless (char *buf,
237 int link_idx,
238 int *link)
239{
240 char *p;
241 int i;
242
243 p = strtok (buf, " \t\n");
244 for (i = 0; p; i++, p = strtok (NULL, " \t\n"))
245 {
246 if (i == link_idx)
247 *link = g_ascii_strtoull (p, NULL, 10);
248 }
249
250 if (i <= link_idx)
251 return FALSE;
252
253 return TRUE;
254}
255
256static inline int
257parse_wireless_header (char *buf)
258{
259 char *p;
260 int i;
261
262 p = strtok (buf, "| \t\n");
263 p = strtok (NULL, "| \t\n"); /* Skip the first one */
264 for (i = 0; p; i++, p = strtok (NULL, "| \t\n"))
265 {
266 if (!strcmp (p, "link"))
267 {
268 return i;
269 }
270 }
271
272 return -1;
273}
274
275static inline FILE *
276get_proc_net_wireless_fh (void)
277{
278 static FILE *retval = NULL;
279
280 if (retval != NULL)
281 return retval;
282
283 return retval = fopen ("/proc/net/wireless", "r");
284}
285
286char *
287netstatus_sysdeps_read_iface_wireless_details (const char *iface,
288 gboolean *is_wireless,
289 int *signal_strength)
290{
291 FILE *fh;
292 char buf [512];
293 int link_idx;
294 char *error_message = NULL;
295
296 g_return_val_if_fail (iface != NULL, NULL);
297 g_return_val_if_fail (is_wireless != NULL, NULL);
298 g_return_val_if_fail (signal_strength != NULL, NULL);
299
300 if (is_wireless)
301 *is_wireless = FALSE;
302 if (signal_strength)
303 *signal_strength = 0;
304
305 fh = get_proc_net_wireless_fh ();
306 if (!fh)
307 return NULL;
308
309 fgets (buf, sizeof (buf), fh);
310 fgets (buf, sizeof (buf), fh);
311
312 link_idx = parse_wireless_header (buf);
313 if (link_idx == -1)
314 return g_strdup (_("Could not parse /proc/net/wireless. Unknown format."));
315
316 while (fgets (buf, sizeof (buf), fh))
317 {
318 char *details;
319 char *name;
320 int link = 0;
321
322 name = buf;
323 while (g_ascii_isspace (name [0]))
324 name++;
325
326 details = parse_iface_name (name);
327 if (!details)
328 {
329 if (!error_message)
330 error_message = g_strdup_printf (_("Could not parse interface name from '%s'"), buf);
331 continue;
332 }
333
334 if (strcmp (name, iface) != 0)
335 continue;
336
337 if (!parse_wireless (details, link_idx, &link))
338 {
339 if (error_message)
340 g_free (error_message);
341 error_message = g_strdup_printf (_("Could not parse wireless details from '%s'. link_idx = %d;"),
342 buf, link_idx);
343 continue;
344 }
345
346 /* Stolen from the wireless applet */
347 *signal_strength = (int) rint ((log (link) / log (92)) * 100.0);
348 *signal_strength = CLAMP (*signal_strength, 0, 100);
349 *is_wireless = TRUE;
350
351 break;
352 }
353
354 rewind (fh);
355 fflush (fh);
356
357 return error_message;
358}
359
360#else /* defined(__FreeBSD__) */
361
362static inline void
363parse_header (char *buf,
364 int *prx_idx,
365 int *ptx_idx,
366 int *brx_idx,
367 int *btx_idx)
368{
369 char *p;
370 int i;
371
372 *prx_idx = *ptx_idx = -1;
373 *brx_idx = *btx_idx = -1;
374
375 p = strtok (buf, " \n\t");
376 for (i = 0; p; i++, p = strtok (NULL, " \t\n"))
377 {
378 if (!strcmp (p, "Ipkts"))
379 {
380 *prx_idx = i;
381 }
382 else if (!strcmp (p, "Ibytes"))
383 {
384 *brx_idx = i;
385 }
386 else if (!strcmp (p, "Opkts"))
387 {
388 *ptx_idx = i;
389 }
390 else if (!strcmp (p, "Obytes"))
391 {
392 *btx_idx = i;
393 }
394 }
395}
396
397static inline gboolean
398wireless_getval (const char *iface,
399 gpointer req,
400 unsigned long req_type,
401 char **error)
402{
403 struct ifreq ifr;
404 int s;
405
406 memset (&ifr, 0, sizeof (ifr));
407
408 strlcpy (ifr.ifr_name, iface, sizeof (ifr.ifr_name));
409 ifr.ifr_data = (caddr_t) req;
410
411 s = socket (AF_INET, SOCK_DGRAM, 0);
412 if (s == -1)
413 {
414 *error = g_strdup_printf (_("Could not connect to interface, '%s'"), iface);
415 return FALSE;
416 }
417
418 if (ioctl (s, req_type, &ifr) == -1)
419 {
420 *error = g_strdup_printf (_("Could not send ioctl to interface, '%s'"), iface);
421 close (s);
422 return FALSE;
423 }
424
425 close (s);
426 return TRUE;
427}
428
429static inline char *
430get_an_data (const char *iface,
431 int *signal_strength)
432{
433#ifdef AN_RID_RSSI_MAP
434 struct an_ltv_rssi_map an_rssimap;
435#endif
436 struct an_req areq;
437 struct an_ltv_status *sts;
438 int level;
439 char *error = NULL;
440 gboolean rssimap_valid = FALSE;
441
442#ifdef AN_RID_RSSI_MAP
443 an_rssimap.an_len = sizeof (an_rssimap);
444 an_rssimap.an_type = AN_RID_RSSI_MAP;
445 rssimap_valid = wireless_getval (iface, (gpointer) &an_rssimap, SIOCGAIRONET, &error);
446#endif
447
448 areq.an_len = sizeof (areq);
449 areq.an_type = AN_RID_STATUS;
450
451 if (!wireless_getval (iface, (gpointer) &areq, SIOCGAIRONET, &error))
452 return error;
453
454 sts = (struct an_ltv_status *) &areq;
455
456#ifdef AN_RID_RSSI_MAP
457 if (rssimap_valid)
458 level = (int) (an_rssimap.an_entries[sts->an_normalized_strength].an_rss_pct);
459 else
460 level = (int) (sts->an_normalized_strength);
461#else
462 level = (int) (sts->an_normalized_rssi);
463#endif
464
465 memcpy (signal_strength, &level, sizeof (signal_strength));
466
467 return error;
468}
469
470static inline char *
471get_wi_data (const char *iface,
472 int *signal_strength)
473{
474 struct wi_req wreq;
475 int level;
476 char *error = NULL;
477
478 memset (&wreq, 0, sizeof (wreq));
479
480 wreq.wi_len = WI_MAX_DATALEN;
481 wreq.wi_type = WI_RID_COMMS_QUALITY;
482
483 if (!wireless_getval (iface, &wreq, SIOCGWAVELAN, &error))
484 return error;
485
486 level = (int) wreq.wi_val[1];
487
488#ifdef WI_RID_READ_APS
489 if (signal_strength <= 0)
490 {
491 /* we fail to get signal strength by usual means, try another way */
492 static time_t last_scan;
493 static long int cached;
494 time_t now;
495
496 now = time (NULL);
497
498 /* XXX: this is long operation, and we will scan station not often then one in 5 secs */
499 if (now > last_scan + 5)
500 {
501 struct wi_apinfo *w;
502 int nstations;
503
504 bzero (&wreq, sizeof (wreq));
505 wreq.wi_len = WI_MAX_DATALEN;
506 wreq.wi_type = WI_RID_READ_APS;
507 if (!wireless_getval (iface, &wreq, SIOCGWAVELAN, &error))
508 return error;
509 nstations = *(int *) wreq.wi_val;
510 if (nstations > 0)
511 {
512 w = (struct wi_apinfo *)(((char *) &wreq.wi_val) + sizeof (int));
513 signal_strength = (long int) w->signal;
514 }
515
516 cached = signal_strength;
517 last_scan = now;
518 }
519 else
520 {
521 signal_strength = cached;
522 }
523 }
524#endif
525
526 memcpy (signal_strength, &level, sizeof (signal_strength));
527
528 return error;
529}
530
531char *
532netstatus_sysdeps_read_iface_wireless_details (const char *iface,
533 gboolean *is_wireless,
534 int *signal_strength)
535{
536 char *error_message = NULL;
537
538 g_return_val_if_fail (iface != NULL, NULL);
539 g_return_val_if_fail (is_wireless != NULL, NULL);
540 g_return_val_if_fail (signal_strength != NULL, NULL);
541
542 if (is_wireless)
543 *is_wireless = FALSE;
544 if (signal_strength)
545 *signal_strength = 0;
546
547 if (g_strncasecmp (iface, "an", 2) &&
548 g_strncasecmp (iface, "wi", 2) &&
549 g_strncasecmp (iface, "ath", 3) &&
550 g_strncasecmp (iface, "ndis", 4) &&
551 g_strncasecmp (iface, "ipw", 3) &&
552 g_strncasecmp (iface, "iwi", 3) &&
553 g_strncasecmp (iface, "acx", 3))
554 return error_message;
555
556 if (g_strncasecmp (iface, "an", 2) == 0)
557 {
558 error_message = get_an_data (iface, signal_strength);
559 *is_wireless = TRUE;
560 }
561 else
562 {
563 error_message = get_wi_data (iface, signal_strength);
564 *is_wireless = TRUE;
565 }
566
567 return error_message;
568}
569
570char *
571netstatus_sysdeps_read_iface_statistics (const char *iface,
572 gulong *in_packets,
573 gulong *out_packets,
574 gulong *in_bytes,
575 gulong *out_bytes)
576{
577 GError *error;
578 char *command_line;
579 char **argv;
580 char *error_message = NULL;
581 int pipe_out;
582
583 g_return_val_if_fail (iface != NULL, NULL);
584 g_return_val_if_fail (in_packets != NULL, NULL);
585 g_return_val_if_fail (out_packets != NULL, NULL);
586 g_return_val_if_fail (in_bytes != NULL, NULL);
587 g_return_val_if_fail (out_bytes != NULL, NULL);
588
589 *in_packets = -1;
590 *out_packets = -1;
591 *in_bytes = -1;
592 *out_bytes = -1;
593
594 error = NULL;
595 command_line = g_strdup_printf ("/usr/bin/netstat -n -I %s -b -f inet", iface);
596 if (!g_shell_parse_argv (command_line, NULL, &argv, &error))
597 {
598 error_message = g_strdup_printf (_("Could not parse command line '%s': %s"),
599 command_line,
600 error->message);
601 g_error_free (error);
602 g_free (command_line);
603
604 return error_message;
605 }
606 g_free (command_line);
607
608 error = NULL;
609 if (g_spawn_async_with_pipes (NULL,
610 argv,
611 NULL,
612 0,
613 NULL,
614 NULL,
615 NULL,
616 NULL,
617 &pipe_out,
618 NULL,
619 &error))
620 {
621 GIOChannel *channel;
622 char *buf;
623 int prx_idx, ptx_idx;
624 int brx_idx, btx_idx;
625
626 channel = g_io_channel_unix_new (pipe_out);
627
628 g_io_channel_read_line (channel, &buf, NULL, NULL, NULL);
629 parse_header (buf, &prx_idx, &ptx_idx, &brx_idx, &btx_idx);
630 g_free (buf);
631
632 if (prx_idx == -1 || ptx_idx == -1 ||
633 brx_idx == -1 || btx_idx == -1)
634 {
635 error_message = g_strdup (_("Could not parse 'netstat' output. Unknown format"));
636 goto error_shutdown;
637 }
638
639 g_io_channel_read_line (channel, &buf, NULL, NULL, NULL);
640
641 if (!parse_stats (buf,
642 prx_idx, ptx_idx, in_packets, out_packets,
643 brx_idx, btx_idx, in_bytes, out_bytes))
644 {
645 error_message = g_strdup_printf (_("Could not parse interface statistics from '%s'. "
646 "prx_idx = %d; ptx_idx = %d; brx_idx = %d; btx_idx = %d;"),
647 buf, prx_idx, ptx_idx, brx_idx, btx_idx);
648 }
649 else if (*in_packets == -1 || *out_packets == -1 || *in_bytes == -1 || *out_bytes == -1)
650 {
651 error_message = g_strdup_printf ("Could not obtain information on interface '%s' from netstat",
652 iface);
653 }
654
655 g_free (buf);
656
657 error_shutdown:
658 g_io_channel_unref (channel);
659 close (pipe_out);
660 }
661 else
662 {
663 error_message = g_strdup_printf ("Error running /usr/bin/netstat for '%s': %s",
664 iface, error->message);
665 g_error_free (error);
666 }
667
668 g_strfreev (argv);
669
670 return error_message;
671}
672
673#endif /* !defined(__FreeBSD__) */