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