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