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