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