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