xref: /freebsd/usr.sbin/ppp/throughput.c (revision 2a4a1db342263067035ce69a4017c645da63455d)
1 /*-
2  * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/types.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <termios.h>
35 #include <time.h>
36 
37 #include "log.h"
38 #include "timer.h"
39 #include "throughput.h"
40 #include "descriptor.h"
41 #include "prompt.h"
42 
43 
44 void
45 throughput_init(struct pppThroughput *t, int period)
46 {
47   t->OctetsIn = t->OctetsOut = t->PacketsIn = t->PacketsOut = 0;
48   t->SamplePeriod = period;
49   t->in.SampleOctets = (long long *)
50     calloc(period, sizeof *t->in.SampleOctets);
51   t->in.OctetsPerSecond = 0;
52   t->out.SampleOctets = (long long *)
53     calloc(period, sizeof *t->out.SampleOctets);
54   t->out.OctetsPerSecond = 0;
55   t->BestOctetsPerSecond = 0;
56   t->nSample = 0;
57   time(&t->BestOctetsPerSecondTime);
58   memset(&t->Timer, '\0', sizeof t->Timer);
59   t->Timer.name = "throughput";
60   t->uptime = 0;
61   t->downtime = 0;
62   t->rolling = 0;
63   t->callback.data = NULL;
64   t->callback.fn = NULL;
65   throughput_stop(t);
66 }
67 
68 void
69 throughput_destroy(struct pppThroughput *t)
70 {
71   if (t && t->in.SampleOctets) {
72     throughput_stop(t);
73     free(t->in.SampleOctets);
74     free(t->out.SampleOctets);
75     t->in.SampleOctets = NULL;
76     t->out.SampleOctets = NULL;
77   }
78 }
79 
80 int
81 throughput_uptime(struct pppThroughput *t)
82 {
83   time_t downat;
84 
85   downat = t->downtime ? t->downtime : time(NULL);
86   if (t->uptime && downat < t->uptime) {
87     /* Euch !  The clock's gone back ! */
88     int i;
89 
90     for (i = 0; i < t->SamplePeriod; i++)
91       t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
92     t->nSample = 0;
93     t->uptime = downat;
94   }
95   return t->uptime ? downat - t->uptime : 0;
96 }
97 
98 void
99 throughput_disp(struct pppThroughput *t, struct prompt *prompt)
100 {
101   int secs_up, divisor;
102 
103   secs_up = throughput_uptime(t);
104   prompt_Printf(prompt, "Connect time: %d:%02d:%02d", secs_up / 3600,
105                 (secs_up / 60) % 60, secs_up % 60);
106   if (t->downtime)
107     prompt_Printf(prompt, " - down at %s", ctime(&t->downtime));
108   else
109     prompt_Printf(prompt, "\n");
110 
111   divisor = secs_up ? secs_up : 1;
112   prompt_Printf(prompt, "%llu octets in, %llu octets out\n",
113                 t->OctetsIn, t->OctetsOut);
114   prompt_Printf(prompt, "%llu packets in, %llu packets out\n",
115                 t->PacketsIn, t->PacketsOut);
116   if (t->rolling) {
117     prompt_Printf(prompt, "  overall   %6qu bytes/sec\n",
118                   (t->OctetsIn + t->OctetsOut) / divisor);
119     prompt_Printf(prompt, "  %s %6qu bytes/sec in, %6qu bytes/sec out "
120                   "(over the last %d secs)\n",
121                   t->downtime ? "average  " : "currently",
122                   t->in.OctetsPerSecond, t->out.OctetsPerSecond,
123                   secs_up > t->SamplePeriod ? t->SamplePeriod : secs_up);
124     prompt_Printf(prompt, "  peak      %6qu bytes/sec on %s",
125                   t->BestOctetsPerSecond, ctime(&t->BestOctetsPerSecondTime));
126   } else
127     prompt_Printf(prompt, "Overall %llu bytes/sec\n",
128                   (t->OctetsIn + t->OctetsOut) / divisor);
129 }
130 
131 
132 void
133 throughput_log(struct pppThroughput *t, int level, const char *title)
134 {
135   if (t->uptime) {
136     int secs_up;
137 
138     secs_up = throughput_uptime(t);
139     if (title == NULL)
140       title = "";
141     log_Printf(level, "%s%sConnect time: %d secs: %llu octets in, %llu octets"
142                " out\n", title, *title ? ": " : "", secs_up, t->OctetsIn,
143                t->OctetsOut);
144     log_Printf(level, "%s%s%llu packets in, %llu packets out\n",
145                title, *title ? ": " : "",  t->PacketsIn, t->PacketsOut);
146     if (secs_up == 0)
147       secs_up = 1;
148     if (t->rolling)
149       log_Printf(level, " total %llu bytes/sec, peak %llu bytes/sec on %s",
150                  (t->OctetsIn + t->OctetsOut) / secs_up, t->BestOctetsPerSecond,
151                  ctime(&t->BestOctetsPerSecondTime));
152     else
153       log_Printf(level, " total %llu bytes/sec\n",
154                  (t->OctetsIn + t->OctetsOut) / secs_up);
155   }
156 }
157 
158 static void
159 throughput_sampler(void *v)
160 {
161   struct pppThroughput *t = (struct pppThroughput *)v;
162   unsigned long long old;
163   int uptime, divisor;
164   unsigned long long octets;
165 
166   timer_Stop(&t->Timer);
167 
168   uptime = throughput_uptime(t);
169   divisor = uptime < t->SamplePeriod ? uptime + 1 : t->SamplePeriod;
170 
171   old = t->in.SampleOctets[t->nSample];
172   t->in.SampleOctets[t->nSample] = t->OctetsIn;
173   t->in.OctetsPerSecond = (t->in.SampleOctets[t->nSample] - old) / divisor;
174 
175   old = t->out.SampleOctets[t->nSample];
176   t->out.SampleOctets[t->nSample] = t->OctetsOut;
177   t->out.OctetsPerSecond = (t->out.SampleOctets[t->nSample] - old) / divisor;
178 
179   octets = t->in.OctetsPerSecond + t->out.OctetsPerSecond;
180   if (t->BestOctetsPerSecond < octets) {
181     t->BestOctetsPerSecond = octets;
182     time(&t->BestOctetsPerSecondTime);
183   }
184 
185   if (++t->nSample == t->SamplePeriod)
186     t->nSample = 0;
187 
188   if (t->callback.fn != NULL && uptime >= t->SamplePeriod)
189     (*t->callback.fn)(t->callback.data);
190 
191   timer_Start(&t->Timer);
192 }
193 
194 void
195 throughput_start(struct pppThroughput *t, const char *name, int rolling)
196 {
197   int i;
198   timer_Stop(&t->Timer);
199 
200   for (i = 0; i < t->SamplePeriod; i++)
201     t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
202   t->nSample = 0;
203   t->OctetsIn = t->OctetsOut = 0;
204   t->in.OctetsPerSecond = t->out.OctetsPerSecond = t->BestOctetsPerSecond = 0;
205   time(&t->BestOctetsPerSecondTime);
206   t->downtime = 0;
207   time(&t->uptime);
208   throughput_restart(t, name, rolling);
209 }
210 
211 void
212 throughput_restart(struct pppThroughput *t, const char *name, int rolling)
213 {
214   timer_Stop(&t->Timer);
215   t->rolling = rolling ? 1 : 0;
216   if (t->rolling) {
217     t->Timer.load = SECTICKS;
218     t->Timer.func = throughput_sampler;
219     t->Timer.name = name;
220     t->Timer.arg = t;
221     timer_Start(&t->Timer);
222   } else {
223     t->Timer.load = 0;
224     t->Timer.func = NULL;
225     t->Timer.name = NULL;
226     t->Timer.arg = NULL;
227   }
228 }
229 
230 void
231 throughput_stop(struct pppThroughput *t)
232 {
233   if (t->Timer.state != TIMER_STOPPED)
234     time(&t->downtime);
235   timer_Stop(&t->Timer);
236 }
237 
238 void
239 throughput_addin(struct pppThroughput *t, long long n)
240 {
241   t->OctetsIn += n;
242   t->PacketsIn++;
243 }
244 
245 void
246 throughput_addout(struct pppThroughput *t, long long n)
247 {
248   t->OctetsOut += n;
249   t->PacketsOut++;
250 }
251 
252 void
253 throughput_clear(struct pppThroughput *t, int clear_type, struct prompt *prompt)
254 {
255   if (clear_type & (THROUGHPUT_OVERALL|THROUGHPUT_CURRENT)) {
256     int i;
257 
258     for (i = 0; i < t->SamplePeriod; i++)
259       t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
260     t->nSample = 0;
261   }
262 
263   if (clear_type & THROUGHPUT_OVERALL) {
264     int divisor;
265 
266     if ((divisor = throughput_uptime(t)) == 0)
267       divisor = 1;
268     prompt_Printf(prompt, "overall cleared (was %6qu bytes/sec)\n",
269                   (t->OctetsIn + t->OctetsOut) / divisor);
270     t->OctetsIn = t->OctetsOut = 0;
271     t->downtime = 0;
272     time(&t->uptime);
273   }
274 
275   if (clear_type & THROUGHPUT_CURRENT) {
276     prompt_Printf(prompt, "current cleared (was %6qu bytes/sec in,"
277                   " %6qu bytes/sec out)\n",
278                   t->in.OctetsPerSecond, t->out.OctetsPerSecond);
279     t->in.OctetsPerSecond = t->out.OctetsPerSecond = 0;
280   }
281 
282   if (clear_type & THROUGHPUT_PEAK) {
283     char *time_buf, *last;
284 
285     time_buf = ctime(&t->BestOctetsPerSecondTime);
286     last = time_buf + strlen(time_buf);
287     if (last > time_buf && *--last == '\n')
288       *last = '\0';
289     prompt_Printf(prompt, "peak    cleared (was %6qu bytes/sec on %s)\n",
290                   t->BestOctetsPerSecond, time_buf);
291     t->BestOctetsPerSecond = 0;
292     time(&t->BestOctetsPerSecondTime);
293   }
294 }
295 
296 void
297 throughput_callback(struct pppThroughput *t, void (*fn)(void *), void *data)
298 {
299   t->callback.fn = fn;
300   t->callback.data = data;
301 }
302