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