kludge for pseudo-glibc on linux/ppc
[ppp.git] / pppstats / pppstats.c
1 /*
2  * print PPP statistics:
3  *      pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]
4  *
5  *   -a Show absolute values rather than deltas
6  *   -d Show data rate (kB/s) rather than bytes
7  *   -v Show more stats for VJ TCP header compression
8  *   -r Show compression ratio
9  *   -z Show compression statistics instead of default display
10  *
11  * History:
12  *      perkins@cps.msu.edu: Added compression statistics and alternate 
13  *                display. 11/94
14  *      Brad Parker (brad@cayman.com) 6/92
15  *
16  * from the original "slstats" by Van Jacobson
17  *
18  * Copyright (c) 1989 Regents of the University of California.
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms are permitted
22  * provided that the above copyright notice and this paragraph are
23  * duplicated in all such forms and that any documentation,
24  * advertising materials, and other materials related to such
25  * distribution and use acknowledge that the software was developed
26  * by the University of California, Berkeley.  The name of the
27  * University may not be used to endorse or promote products derived
28  * from this software without specific prior written permission.
29  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
30  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
31  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
32  */
33
34 #ifndef lint
35 static char rcsid[] = "$Id: pppstats.c,v 1.24 1999/01/20 00:02:21 paulus Exp $";
36 #endif
37
38 #include <stdio.h>
39 #include <stddef.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <signal.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <sys/param.h>
48 #include <sys/types.h>
49 #include <sys/ioctl.h>
50
51 #ifndef STREAMS
52 #if defined(_linux_) && defined(__powerpc__)
53 /* kludge alert! */
54 #undef __GLIBC__
55 #endif
56 #include <sys/socket.h>         /* *BSD, Linux, NeXT, Ultrix etc. */
57 #ifndef _linux_
58 #include <net/if.h>
59 #include <net/ppp_defs.h>
60 #include <net/if_ppp.h>
61 #else
62 /* Linux */
63 #if __GLIBC__ >= 2
64 #include <asm/types.h>          /* glibc 2 conflicts with linux/types.h */
65 #include <net/if.h>
66 #else
67 #include <linux/types.h>
68 #include <linux/if.h>
69 #endif
70 #include <linux/ppp_defs.h>
71 #include <linux/if_ppp.h>
72 #endif /* _linux_ */
73
74 #else   /* STREAMS */
75 #include <sys/stropts.h>        /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
76 #include <net/ppp_defs.h>
77 #include <net/pppio.h>
78
79 #endif  /* STREAMS */
80
81 int     vflag, rflag, zflag;    /* select type of display */
82 int     aflag;                  /* print absolute values, not deltas */
83 int     dflag;                  /* print data rates, not bytes */
84 int     interval, count;
85 int     infinite;
86 int     unit;
87 int     s;                      /* socket or /dev/ppp file descriptor */
88 int     signalled;              /* set if alarm goes off "early" */
89 char    *progname;
90 char    *interface;
91
92 #if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT)
93 extern int optind;
94 extern char *optarg;
95 #endif
96
97 static void usage __P((void));
98 static void catchalarm __P((int));
99 static void get_ppp_stats __P((struct ppp_stats *));
100 static void get_ppp_cstats __P((struct ppp_comp_stats *));
101 static void intpr __P((void));
102
103 int main __P((int, char *argv[]));
104
105 static void
106 usage()
107 {
108     fprintf(stderr, "Usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n",
109             progname);
110     exit(1);
111 }
112
113 /*
114  * Called if an interval expires before intpr has completed a loop.
115  * Sets a flag to not wait for the alarm.
116  */
117 static void
118 catchalarm(arg)
119     int arg;
120 {
121     signalled = 1;
122 }
123
124
125 #ifndef STREAMS
126 static void
127 get_ppp_stats(curp)
128     struct ppp_stats *curp;
129 {
130     struct ifpppstatsreq req;
131
132     memset (&req, 0, sizeof (req));
133
134 #ifdef _linux_
135     req.stats_ptr = (caddr_t) &req.stats;
136 #undef ifr_name
137 #define ifr_name ifr__name
138 #endif
139
140     strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
141     if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
142         fprintf(stderr, "%s: ", progname);
143         if (errno == ENOTTY)
144             fprintf(stderr, "kernel support missing\n");
145         else
146             perror("couldn't get PPP statistics");
147         exit(1);
148     }
149     *curp = req.stats;
150 }
151
152 static void
153 get_ppp_cstats(csp)
154     struct ppp_comp_stats *csp;
155 {
156     struct ifpppcstatsreq creq;
157
158     memset (&creq, 0, sizeof (creq));
159
160 #ifdef _linux_
161     creq.stats_ptr = (caddr_t) &creq.stats;
162 #undef  ifr_name
163 #define ifr_name ifr__name
164 #endif
165
166     strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
167     if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
168         fprintf(stderr, "%s: ", progname);
169         if (errno == ENOTTY) {
170             fprintf(stderr, "no kernel compression support\n");
171             if (zflag)
172                 exit(1);
173             rflag = 0;
174         } else {
175             perror("couldn't get PPP compression stats");
176             exit(1);
177         }
178     }
179
180 #ifdef _linux_
181     if (creq.stats.c.bytes_out == 0) {
182         creq.stats.c.bytes_out = creq.stats.c.comp_bytes + creq.stats.c.inc_bytes;
183         creq.stats.c.in_count = creq.stats.c.unc_bytes;
184     }
185     if (creq.stats.c.bytes_out == 0)
186         creq.stats.c.ratio = 0.0;
187     else
188         creq.stats.c.ratio = 256.0 * creq.stats.c.in_count /
189                              creq.stats.c.bytes_out;
190
191     if (creq.stats.d.bytes_out == 0) {
192         creq.stats.d.bytes_out = creq.stats.d.comp_bytes + creq.stats.d.inc_bytes;
193         creq.stats.d.in_count = creq.stats.d.unc_bytes;
194     }
195     if (creq.stats.d.bytes_out == 0)
196         creq.stats.d.ratio = 0.0;
197     else
198         creq.stats.d.ratio = 256.0 * creq.stats.d.in_count /
199                              creq.stats.d.bytes_out;
200 #endif
201
202     *csp = creq.stats;
203 }
204
205 #else   /* STREAMS */
206
207 int
208 strioctl(fd, cmd, ptr, ilen, olen)
209     int fd, cmd, ilen, olen;
210     char *ptr;
211 {
212     struct strioctl str;
213
214     str.ic_cmd = cmd;
215     str.ic_timout = 0;
216     str.ic_len = ilen;
217     str.ic_dp = ptr;
218     if (ioctl(fd, I_STR, &str) == -1)
219         return -1;
220     if (str.ic_len != olen)
221         fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
222                olen, str.ic_len, cmd);
223     return 0;
224 }
225
226 static void
227 get_ppp_stats(curp)
228     struct ppp_stats *curp;
229 {
230     if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) {
231         fprintf(stderr, "%s: ", progname);
232         if (errno == EINVAL)
233             fprintf(stderr, "kernel support missing\n");
234         else
235             perror("couldn't get PPP statistics");
236         exit(1);
237     }
238 }
239
240 static void
241 get_ppp_cstats(csp)
242     struct ppp_comp_stats *csp;
243 {
244     if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) {
245         fprintf(stderr, "%s: ", progname);
246         if (errno == ENOTTY) {
247             fprintf(stderr, "no kernel compression support\n");
248             if (zflag)
249                 exit(1);
250             rflag = 0;
251         } else {
252             perror("couldn't get PPP compression statistics");
253             exit(1);
254         }
255     }
256 }
257
258 #endif /* STREAMS */
259
260 #define MAX0(a)         ((int)(a) > 0? (a): 0)
261 #define V(offset)       MAX0(cur.offset - old.offset)
262 #define W(offset)       MAX0(ccs.offset - ocs.offset)
263
264 #define RATIO(c, i, u)  ((c) == 0? 1.0: (u) / ((double)(c) + (i)))
265 #define CRATE(x)        RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
266
267 #define KBPS(n)         ((n) / (interval * 1000.0))
268
269 /*
270  * Print a running summary of interface statistics.
271  * Repeat display every interval seconds, showing statistics
272  * collected over that interval.  Assumes that interval is non-zero.
273  * First line printed is cumulative.
274  */
275 static void
276 intpr()
277 {
278     register int line = 0;
279     sigset_t oldmask, mask;
280     char *bunit;
281     int ratef = 0;
282     struct ppp_stats cur, old;
283     struct ppp_comp_stats ccs, ocs;
284
285     memset(&old, 0, sizeof(old));
286     memset(&ocs, 0, sizeof(ocs));
287
288     while (1) {
289         get_ppp_stats(&cur);
290         if (zflag || rflag)
291             get_ppp_cstats(&ccs);
292
293         (void)signal(SIGALRM, catchalarm);
294         signalled = 0;
295         (void)alarm(interval);
296
297         if ((line % 20) == 0) {
298             if (zflag) {
299                 printf("IN:  COMPRESSED  INCOMPRESSIBLE   COMP | ");
300                 printf("OUT: COMPRESSED  INCOMPRESSIBLE   COMP\n");
301                 bunit = dflag? "KB/S": "BYTE";
302                 printf("    %s   PACK     %s   PACK  RATIO | ", bunit, bunit);
303                 printf("    %s   PACK     %s   PACK  RATIO", bunit, bunit);
304             } else {
305                 printf("%8.8s %6.6s %6.6s",
306                        "IN", "PACK", "VJCOMP");
307
308                 if (!rflag)
309                     printf(" %6.6s %6.6s", "VJUNC", "VJERR");
310                 if (vflag)
311                     printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
312                 if (rflag)
313                     printf(" %6.6s %6.6s", "RATIO", "UBYTE");
314                 printf("  | %8.8s %6.6s %6.6s",
315                        "OUT", "PACK", "VJCOMP");
316
317                 if (!rflag)
318                     printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
319                 if (vflag)
320                     printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
321                 if (rflag)
322                     printf(" %6.6s %6.6s", "RATIO", "UBYTE");
323             }
324             putchar('\n');
325         }
326
327         if (zflag) {
328             if (ratef) {
329                 printf("%8.3f %6u %8.3f %6u %6.2f",
330                        KBPS(W(d.comp_bytes)),
331                        W(d.comp_packets),
332                        KBPS(W(d.inc_bytes)),
333                        W(d.inc_packets),
334                        ccs.d.ratio / 256.0);
335                 printf(" | %8.3f %6u %8.3f %6u %6.2f",
336                        KBPS(W(c.comp_bytes)),
337                        W(c.comp_packets),
338                        KBPS(W(c.inc_bytes)),
339                        W(c.inc_packets),
340                        ccs.c.ratio / 256.0);
341             } else {
342                 printf("%8u %6u %8u %6u %6.2f",
343                        W(d.comp_bytes),
344                        W(d.comp_packets),
345                        W(d.inc_bytes),
346                        W(d.inc_packets),
347                        ccs.d.ratio / 256.0);
348                 printf(" | %8u %6u %8u %6u %6.2f",
349                        W(c.comp_bytes),
350                        W(c.comp_packets),
351                        W(c.inc_bytes),
352                        W(c.inc_packets),
353                        ccs.c.ratio / 256.0);
354             }
355         
356         } else {
357             if (ratef)
358                 printf("%8.3f", KBPS(V(p.ppp_ibytes)));
359             else
360                 printf("%8u", V(p.ppp_ibytes));
361             printf(" %6u %6u",
362                    V(p.ppp_ipackets),
363                    V(vj.vjs_compressedin));
364             if (!rflag)
365                 printf(" %6u %6u",
366                        V(vj.vjs_uncompressedin),
367                        V(vj.vjs_errorin));
368             if (vflag)
369                 printf(" %6u %6u",
370                        V(vj.vjs_tossed),
371                        V(p.ppp_ipackets) - V(vj.vjs_compressedin)
372                        - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
373             if (rflag) {
374                 printf(" %6.2f ", CRATE(d));
375                 if (ratef)
376                     printf("%6.2f", KBPS(W(d.unc_bytes)));
377                 else
378                     printf("%6u", W(d.unc_bytes));
379             }
380             if (ratef)
381                 printf("  | %8.3f", KBPS(V(p.ppp_obytes)));
382             else
383                 printf("  | %8u", V(p.ppp_obytes));
384             printf(" %6u %6u",
385                    V(p.ppp_opackets),
386                    V(vj.vjs_compressed));
387             if (!rflag)
388                 printf(" %6u %6u",
389                        V(vj.vjs_packets) - V(vj.vjs_compressed),
390                        V(p.ppp_opackets) - V(vj.vjs_packets));
391             if (vflag)
392                 printf(" %6u %6u",
393                        V(vj.vjs_searches),
394                        V(vj.vjs_misses));
395             if (rflag) {
396                 printf(" %6.2f ", CRATE(c));
397                 if (ratef)
398                     printf("%6.2f", KBPS(W(c.unc_bytes)));
399                 else
400                     printf("%6u", W(c.unc_bytes));
401             }
402
403         }
404
405         putchar('\n');
406         fflush(stdout);
407         line++;
408
409         count--;
410         if (!infinite && !count)
411             break;
412
413         sigemptyset(&mask);
414         sigaddset(&mask, SIGALRM);
415         sigprocmask(SIG_BLOCK, &mask, &oldmask);
416         if (!signalled) {
417             sigemptyset(&mask);
418             sigsuspend(&mask);
419         }
420         sigprocmask(SIG_SETMASK, &oldmask, NULL);
421         signalled = 0;
422         (void)alarm(interval);
423
424         if (!aflag) {
425             old = cur;
426             ocs = ccs;
427             ratef = dflag;
428         }
429     }
430 }
431
432 int
433 main(argc, argv)
434     int argc;
435     char *argv[];
436 {
437     int c;
438 #ifdef STREAMS
439     char *dev;
440 #endif
441
442     interface = "ppp0";
443     if ((progname = strrchr(argv[0], '/')) == NULL)
444         progname = argv[0];
445     else
446         ++progname;
447
448     while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
449         switch (c) {
450         case 'a':
451             ++aflag;
452             break;
453         case 'd':
454             ++dflag;
455             break;
456         case 'v':
457             ++vflag;
458             break;
459         case 'r':
460             ++rflag;
461             break;
462         case 'z':
463             ++zflag;
464             break;
465         case 'c':
466             count = atoi(optarg);
467             if (count <= 0)
468                 usage();
469             break;
470         case 'w':
471             interval = atoi(optarg);
472             if (interval <= 0)
473                 usage();
474             break;
475         default:
476             usage();
477         }
478     }
479     argc -= optind;
480     argv += optind;
481
482     if (!interval && count)
483         interval = 5;
484     if (interval && !count)
485         infinite = 1;
486     if (!interval && !count)
487         count = 1;
488     if (aflag)
489         dflag = 0;
490
491     if (argc > 1)
492         usage();
493     if (argc > 0)
494         interface = argv[0];
495
496     if (sscanf(interface, "ppp%d", &unit) != 1) {
497         fprintf(stderr, "%s: invalid interface '%s' specified\n",
498                 progname, interface);
499     }
500
501 #ifndef STREAMS
502     {
503         struct ifreq ifr;
504
505         s = socket(AF_INET, SOCK_DGRAM, 0);
506         if (s < 0) {
507             fprintf(stderr, "%s: ", progname);
508             perror("couldn't create IP socket");
509             exit(1);
510         }
511
512 #ifdef _linux_
513 #undef  ifr_name
514 #define ifr_name ifr_ifrn.ifrn_name
515 #endif
516         strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
517         if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
518             fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
519                     progname, interface);
520             exit(1);
521         }
522     }
523
524 #else   /* STREAMS */
525 #ifdef __osf__
526     dev = "/dev/streams/ppp";
527 #else
528     dev = "/dev/ppp";
529 #endif
530     if ((s = open(dev, O_RDONLY)) < 0) {
531         fprintf(stderr, "%s: couldn't open ", progname);
532         perror(dev);
533         exit(1);
534     }
535     if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) {
536         fprintf(stderr, "%s: ppp%d is not available\n", progname, unit);
537         exit(1);
538     }
539
540 #endif  /* STREAMS */
541
542     intpr();
543     exit(0);
544 }