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