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