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