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