updated to be much closer to NetBSD version
[ppp.git] / pppstats / pppstats.c
1 /*
2  * print PPP statistics:
3  *      pppstats [-a] [-v] [-r] [-z] [-c count] [-w wait] [interface]
4  *
5  *   -a Show absolute values rather than deltas
6  *   -v Verbose mode for default display
7  *   -r Show compression ratio in default display
8  *   -z Show compression statistics instead of default display
9  *
10  * History:
11  *      perkins@cps.msu.edu: Added compression statistics and alternate 
12  *                display. 11/94
13  *      Brad Parker (brad@cayman.com) 6/92
14  *
15  * from the original "slstats" by Van Jacobson
16  *
17  * Copyright (c) 1989 Regents of the University of California.
18  * All rights reserved.
19  *
20  * Redistribution and use in source and binary forms are permitted
21  * provided that the above copyright notice and this paragraph are
22  * duplicated in all such forms and that any documentation,
23  * advertising materials, and other materials related to such
24  * distribution and use acknowledge that the software was developed
25  * by the University of California, Berkeley.  The name of the
26  * University may not be used to endorse or promote products derived
27  * from this software without specific prior written permission.
28  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31  */
32
33 #ifndef lint
34 static char rcsid[] = "$Id: pppstats.c,v 1.13 1996/07/01 01:22:36 paulus Exp $";
35 #endif
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 <net/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/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     interval, count;
65 int     infinite;
66 int     unit;
67 int     s;                      /* socket or /dev/ppp file descriptor */
68 int     signalled;              /* set if alarm goes off "early" */
69 char    *progname;
70 char    *interface;
71
72 void
73 usage()
74 {
75     fprintf(stderr, "Usage: %s [-v|-r|-z] [-a] [-c count] [-w wait] [interface]\n",
76             progname);
77     exit(1);
78 }
79
80 /*
81  * Called if an interval expires before intpr has completed a loop.
82  * Sets a flag to not wait for the alarm.
83  */
84 void
85 catchalarm(arg)
86     int arg;
87 {
88     signalled = 1;
89 }
90
91
92 #ifndef STREAMS
93 void
94 get_ppp_stats(curp)
95     struct ppp_stats *curp;
96 {
97     struct ifpppstatsreq req;
98
99     memset (&req, 0, sizeof (req));
100
101 #ifdef _linux_
102     req.stats_ptr = (caddr_t) &req.stats;
103 #undef ifr_name
104 #define ifr_name ifr__name
105 #endif
106
107     strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
108     if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
109         fprintf(stderr, "%s: ", progname);
110         if (errno == ENOTTY)
111             fprintf(stderr, "kernel support missing\n");
112         else
113             perror("couldn't get PPP statistics");
114         exit(1);
115     }
116     *curp = req.stats;
117 }
118
119 void
120 get_ppp_cstats(csp)
121     struct ppp_comp_stats *csp;
122 {
123     struct ifpppcstatsreq creq;
124
125     memset (&creq, 0, sizeof (creq));
126
127 #ifdef _linux_
128     creq.stats_ptr = (caddr_t) &creq.stats;
129 #undef  ifr_name
130 #define ifr_name ifr__name
131 #endif
132
133     strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
134     if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
135         fprintf(stderr, "%s: ", progname);
136         if (errno == ENOTTY) {
137             fprintf(stderr, "no kernel compression support\n");
138             if (zflag)
139                 exit(1);
140             rflag = 0;
141         } else {
142             perror("couldn't get PPP compression stats");
143             exit(1);
144         }
145     }
146
147 #ifdef _linux_
148     if (creq.stats.c.bytes_out == 0)
149         creq.stats.c.ratio = 0.0;
150     else
151         creq.stats.c.ratio = (double) creq.stats.c.in_count /
152                              (double) creq.stats.c.bytes_out;
153
154     if (creq.stats.d.bytes_out == 0)
155         creq.stats.d.ratio = 0.0;
156     else
157         creq.stats.d.ratio = (double) creq.stats.d.in_count /
158                              (double) creq.stats.d.bytes_out;
159 #endif
160
161     *csp = creq.stats;
162 }
163
164 #else   /* STREAMS */
165
166 int
167 strioctl(fd, cmd, ptr, ilen, olen)
168     int fd, cmd, ilen, olen;
169     char *ptr;
170 {
171     struct strioctl str;
172
173     str.ic_cmd = cmd;
174     str.ic_timout = 0;
175     str.ic_len = ilen;
176     str.ic_dp = ptr;
177     if (ioctl(fd, I_STR, &str) == -1)
178         return -1;
179     if (str.ic_len != olen)
180         fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
181                olen, str.ic_len, cmd);
182     return 0;
183 }
184
185 void
186 get_ppp_stats(curp)
187     struct ppp_stats *curp;
188 {
189     if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) {
190         fprintf(stderr, "%s: ", progname);
191         if (errno == EINVAL)
192             fprintf(stderr, "kernel support missing\n");
193         else
194             perror("couldn't get PPP statistics");
195         exit(1);
196     }
197 }
198
199 void
200 get_ppp_cstats(csp)
201     struct ppp_comp_stats *csp;
202 {
203     if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) {
204         fprintf(stderr, "%s: ", progname);
205         if (errno == ENOTTY) {
206             fprintf(stderr, "no kernel compression support\n");
207             if (zflag)
208                 exit(1);
209             rflag = 0;
210         } else {
211             perror("couldn't get PPP compression statistics");
212             exit(1);
213         }
214     }
215 }
216
217 #endif /* STREAMS */
218
219 #define MAX0(a)         ((int)(a) > 0? (a): 0)
220 #define V(offset)       MAX0(cur.offset - old.offset)
221 #define W(offset)       MAX0(ccs.offset - ocs.offset)
222
223 #define RATIO(c, i, u)  ((c) == 0? 1.0: (u) / ((double)(c) + (i)))
224 #define CRATE(x)        RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
225
226 /*
227  * Print a running summary of interface statistics.
228  * Repeat display every interval seconds, showing statistics
229  * collected over that interval.  Assumes that interval is non-zero.
230  * First line printed is cumulative.
231  */
232 void
233 intpr()
234 {
235     register int line = 0;
236     sigset_t oldmask, mask;
237     struct ppp_stats cur, old;
238     struct ppp_comp_stats ccs, ocs;
239
240     memset(&old, 0, sizeof(old));
241     memset(&ocs, 0, sizeof(ocs));
242
243     while (1) {
244         get_ppp_stats(&cur);
245         if (zflag || rflag)
246             get_ppp_cstats(&ccs);
247
248         (void)signal(SIGALRM, catchalarm);
249         signalled = 0;
250         (void)alarm(interval);
251
252         if ((line % 20) == 0) {
253             if (zflag) {
254                 printf("IN:  COMPRESSED  INCOMPRESSIBLE   COMP | ");
255                 printf("OUT: COMPRESSED  INCOMPRESSIBLE   COMP\n");
256                 printf("    BYTE   PACK     BYTE   PACK  RATIO | ");
257                 printf("    BYTE   PACK     BYTE   PACK  RATIO");
258             } else {
259                 printf("%8.8s %6.6s %6.6s",
260                        "IN", "PACK", "VJCOMP");
261
262                 if (!rflag)
263                     printf(" %6.6s %6.6s", "VJUNC", "VJERR");
264                 if (vflag)
265                     printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
266                 if (rflag)
267                     printf(" %6.6s %6.6s", "RATIO", "UBYTE");
268                 printf("  | %8.8s %6.6s %6.6s",
269                        "OUT", "PACK", "VJCOMP");
270
271                 if (!rflag)
272                     printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
273                 if (vflag)
274                     printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
275                 if (rflag)
276                     printf(" %6.6s %6.6s", "RATIO", "UBYTE");
277             }
278             putchar('\n');
279         }
280
281         if (zflag) {
282             printf("%8u %6u %8u %6u %6.2f",
283                    W(d.comp_bytes),
284                    W(d.comp_packets),
285                    W(d.inc_bytes),
286                    W(d.inc_packets),
287                    ccs.d.ratio * 256.0);
288
289             printf(" | %8u %6u %8u %6u %6.2f",
290                    W(c.comp_bytes),
291                    W(c.comp_packets),
292                    W(c.inc_bytes),
293                    W(c.inc_packets),
294                    ccs.c.ratio * 256.0);
295         
296         } else {
297
298             printf("%8u %6u %6u",
299                    V(p.ppp_ibytes),
300                    V(p.ppp_ipackets),
301                    V(vj.vjs_compressedin));
302             if (!rflag)
303                 printf(" %6u %6u",
304                        V(vj.vjs_uncompressedin),
305                        V(vj.vjs_errorin));
306             if (vflag)
307                 printf(" %6u %6u",
308                        V(vj.vjs_tossed),
309                        V(p.ppp_ipackets) - V(vj.vjs_compressedin)
310                        - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
311             if (rflag)
312                 printf(" %6.2f %6u",
313                        CRATE(d),
314                        W(d.unc_bytes));
315             printf("  | %8u %6u %6u",
316                    V(p.ppp_obytes),
317                    V(p.ppp_opackets),
318                    V(vj.vjs_compressed));
319             if (!rflag)
320                 printf(" %6u %6u",
321                        V(vj.vjs_packets) - V(vj.vjs_compressed),
322                        V(p.ppp_opackets) - V(vj.vjs_packets));
323             if (vflag)
324                 printf(" %6u %6u",
325                        V(vj.vjs_searches),
326                        V(vj.vjs_misses));
327
328             if (rflag)
329                 printf(" %6.2f %6u",
330                        CRATE(c),
331                        W(c.unc_bytes));
332
333         }
334
335         putchar('\n');
336         fflush(stdout);
337         line++;
338
339         count--;
340         if (!infinite && !count)
341             break;
342
343         sigemptyset(&mask);
344         sigaddset(&mask, SIGALRM);
345         sigprocmask(SIG_BLOCK, &mask, &oldmask);
346         if (!signalled) {
347             sigemptyset(&mask);
348             sigsuspend(&mask);
349         }
350         sigprocmask(SIG_SETMASK, &oldmask, NULL);
351         signalled = 0;
352         (void)alarm(interval);
353
354         if (!aflag) {
355             old = cur;
356             ocs = ccs;
357         }
358     }
359 }
360
361 int
362 main(argc, argv)
363     int argc;
364     char *argv[];
365 {
366     int c;
367
368     interface = "ppp0";
369     if ((progname = strrchr(argv[0], '/')) == NULL)
370         progname = argv[0];
371     else
372         ++progname;
373
374     while ((c = getopt(argc, argv, "avrzc:w:")) != -1) {
375         switch (c) {
376         case 'a':
377             ++aflag;
378             break;
379         case 'v':
380             ++vflag;
381             break;
382         case 'r':
383             ++rflag;
384             break;
385         case 'z':
386             ++zflag;
387             break;
388         case 'c':
389             count = atoi(optarg);
390             if (count <= 0)
391                 usage();
392             break;
393         case 'w':
394             interval = atoi(optarg);
395             if (interval <= 0)
396                 usage();
397             break;
398         default:
399             usage();
400         }
401     }
402     argc -= optind;
403     argv += optind;
404
405     if (!interval && count)
406         interval = 5;
407     if (interval && !count)
408         infinite = 1;
409     if (!interval && !count)
410         count = 1;
411
412     if (argc > 1)
413         usage();
414     if (argc > 0)
415         interface = argv[0];
416
417     if (sscanf(interface, "ppp%d", &unit) != 1) {
418         fprintf(stderr, "%s: invalid interface '%s' specified\n",
419                 progname, interface);
420     }
421
422 #ifndef STREAMS
423     {
424         struct ifreq ifr;
425
426         s = socket(AF_INET, SOCK_DGRAM, 0);
427         if (s < 0) {
428             fprintf(stderr, "%s: ", progname);
429             perror("couldn't create IP socket");
430             exit(1);
431         }
432         strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
433         if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
434             fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
435                     progname, interface);
436             exit(1);
437         }
438     }
439
440 #else   /* STREAMS */
441     if ((s = open("/dev/ppp", O_RDONLY)) < 0) {
442         fprintf(stderr, "%s: ", progname);
443         perror("couldn't open /dev/ppp");
444         exit(1);
445     }
446     if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) {
447         fprintf(stderr, "%s: ppp%d is not available\n", progname, unit);
448         exit(1);
449     }
450
451 #endif  /* STREAMS */
452
453     intpr();
454     exit(0);
455 }