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