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