* Sync comment with code's reality.
[dragonfly.git] / usr.sbin / ppp / throughput.c
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: src/usr.sbin/ppp/throughput.c,v 1.13.2.3 2002/09/01 02:12:32 brian Exp $
27  * $DragonFly: src/usr.sbin/ppp/throughput.c,v 1.2 2003/06/17 04:30:01 dillon Exp $
28  */
29
30 #include <sys/types.h>
31
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <termios.h>
37 #include <time.h>
38
39 #include "log.h"
40 #include "timer.h"
41 #include "throughput.h"
42 #include "descriptor.h"
43 #include "prompt.h"
44
45
46 void
47 throughput_init(struct pppThroughput *t, int period)
48 {
49   t->OctetsIn = t->OctetsOut = t->PacketsIn = t->PacketsOut = 0;
50   t->SamplePeriod = period;
51   t->in.SampleOctets = (long long *)
52     calloc(period, sizeof *t->in.SampleOctets);
53   t->in.OctetsPerSecond = 0;
54   t->out.SampleOctets = (long long *)
55     calloc(period, sizeof *t->out.SampleOctets);
56   t->out.OctetsPerSecond = 0;
57   t->BestOctetsPerSecond = 0;
58   t->nSample = 0;
59   time(&t->BestOctetsPerSecondTime);
60   memset(&t->Timer, '\0', sizeof t->Timer);
61   t->Timer.name = "throughput";
62   t->uptime = 0;
63   t->downtime = 0;
64   t->rolling = 0;
65   t->callback.data = NULL;
66   t->callback.fn = NULL;
67   throughput_stop(t);
68 }
69
70 void
71 throughput_destroy(struct pppThroughput *t)
72 {
73   if (t && t->in.SampleOctets) {
74     throughput_stop(t);
75     free(t->in.SampleOctets);
76     free(t->out.SampleOctets);
77     t->in.SampleOctets = NULL;
78     t->out.SampleOctets = NULL;
79   }
80 }
81
82 int
83 throughput_uptime(struct pppThroughput *t)
84 {
85   time_t downat;
86
87   downat = t->downtime ? t->downtime : time(NULL);
88   if (t->uptime && downat < t->uptime) {
89     /* Euch !  The clock's gone back ! */
90     int i;
91
92     for (i = 0; i < t->SamplePeriod; i++)
93       t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
94     t->nSample = 0;
95     t->uptime = downat;
96   }
97   return t->uptime ? downat - t->uptime : 0;
98 }
99
100 void
101 throughput_disp(struct pppThroughput *t, struct prompt *prompt)
102 {
103   int secs_up, divisor;
104
105   secs_up = throughput_uptime(t);
106   prompt_Printf(prompt, "Connect time: %d:%02d:%02d", secs_up / 3600,
107                 (secs_up / 60) % 60, secs_up % 60);
108   if (t->downtime)
109     prompt_Printf(prompt, " - down at %s", ctime(&t->downtime));
110   else
111     prompt_Printf(prompt, "\n");
112
113   divisor = secs_up ? secs_up : 1;
114   prompt_Printf(prompt, "%llu octets in, %llu octets out\n",
115                 t->OctetsIn, t->OctetsOut);
116   prompt_Printf(prompt, "%llu packets in, %llu packets out\n",
117                 t->PacketsIn, t->PacketsOut);
118   if (t->rolling) {
119     prompt_Printf(prompt, "  overall   %6qu bytes/sec\n",
120                   (t->OctetsIn + t->OctetsOut) / divisor);
121     prompt_Printf(prompt, "  %s %6qu bytes/sec in, %6qu bytes/sec out "
122                   "(over the last %d secs)\n",
123                   t->downtime ? "average  " : "currently",
124                   t->in.OctetsPerSecond, t->out.OctetsPerSecond,
125                   secs_up > t->SamplePeriod ? t->SamplePeriod : secs_up);
126     prompt_Printf(prompt, "  peak      %6qu bytes/sec on %s",
127                   t->BestOctetsPerSecond, ctime(&t->BestOctetsPerSecondTime));
128   } else
129     prompt_Printf(prompt, "Overall %llu bytes/sec\n",
130                   (t->OctetsIn + t->OctetsOut) / divisor);
131 }
132
133
134 void
135 throughput_log(struct pppThroughput *t, int level, const char *title)
136 {
137   if (t->uptime) {
138     int secs_up;
139
140     secs_up = throughput_uptime(t);
141     if (title == NULL)
142       title = "";
143     log_Printf(level, "%s%sConnect time: %d secs: %llu octets in, %llu octets"
144                " out\n", title, *title ? ": " : "", secs_up, t->OctetsIn,
145                t->OctetsOut);
146     log_Printf(level, "%s%s%llu packets in, %llu packets out\n",
147                title, *title ? ": " : "",  t->PacketsIn, t->PacketsOut);
148     if (secs_up == 0)
149       secs_up = 1;
150     if (t->rolling)
151       log_Printf(level, " total %llu bytes/sec, peak %llu bytes/sec on %s",
152                  (t->OctetsIn + t->OctetsOut) / secs_up, t->BestOctetsPerSecond,
153                  ctime(&t->BestOctetsPerSecondTime));
154     else
155       log_Printf(level, " total %llu bytes/sec\n",
156                  (t->OctetsIn + t->OctetsOut) / secs_up);
157   }
158 }
159
160 static void
161 throughput_sampler(void *v)
162 {
163   struct pppThroughput *t = (struct pppThroughput *)v;
164   unsigned long long old;
165   int uptime, divisor;
166   unsigned long long octets;
167
168   timer_Stop(&t->Timer);
169
170   uptime = throughput_uptime(t);
171   divisor = uptime < t->SamplePeriod ? uptime + 1 : t->SamplePeriod;
172
173   old = t->in.SampleOctets[t->nSample];
174   t->in.SampleOctets[t->nSample] = t->OctetsIn;
175   t->in.OctetsPerSecond = (t->in.SampleOctets[t->nSample] - old) / divisor;
176
177   old = t->out.SampleOctets[t->nSample];
178   t->out.SampleOctets[t->nSample] = t->OctetsOut;
179   t->out.OctetsPerSecond = (t->out.SampleOctets[t->nSample] - old) / divisor;
180
181   octets = t->in.OctetsPerSecond + t->out.OctetsPerSecond;
182   if (t->BestOctetsPerSecond < octets) {
183     t->BestOctetsPerSecond = octets;
184     time(&t->BestOctetsPerSecondTime);
185   }
186
187   if (++t->nSample == t->SamplePeriod)
188     t->nSample = 0;
189
190   if (t->callback.fn != NULL && uptime >= t->SamplePeriod)
191     (*t->callback.fn)(t->callback.data);
192
193   timer_Start(&t->Timer);
194 }
195
196 void
197 throughput_start(struct pppThroughput *t, const char *name, int rolling)
198 {
199   int i;
200   timer_Stop(&t->Timer);
201
202   for (i = 0; i < t->SamplePeriod; i++)
203     t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
204   t->nSample = 0;
205   t->OctetsIn = t->OctetsOut = 0;
206   t->in.OctetsPerSecond = t->out.OctetsPerSecond = t->BestOctetsPerSecond = 0;
207   time(&t->BestOctetsPerSecondTime);
208   t->downtime = 0;
209   time(&t->uptime);
210   throughput_restart(t, name, rolling);
211 }
212
213 void
214 throughput_restart(struct pppThroughput *t, const char *name, int rolling)
215 {
216   timer_Stop(&t->Timer);
217   t->rolling = rolling ? 1 : 0;
218   if (t->rolling) {
219     t->Timer.load = SECTICKS;
220     t->Timer.func = throughput_sampler;
221     t->Timer.name = name;
222     t->Timer.arg = t;
223     timer_Start(&t->Timer);
224   } else {
225     t->Timer.load = 0;
226     t->Timer.func = NULL;
227     t->Timer.name = NULL;
228     t->Timer.arg = NULL;
229   }
230 }
231
232 void
233 throughput_stop(struct pppThroughput *t)
234 {
235   if (t->Timer.state != TIMER_STOPPED)
236     time(&t->downtime);
237   timer_Stop(&t->Timer);
238 }
239
240 void
241 throughput_addin(struct pppThroughput *t, long long n)
242 {
243   t->OctetsIn += n;
244   t->PacketsIn++;
245 }
246
247 void
248 throughput_addout(struct pppThroughput *t, long long n)
249 {
250   t->OctetsOut += n;
251   t->PacketsOut++;
252 }
253
254 void
255 throughput_clear(struct pppThroughput *t, int clear_type, struct prompt *prompt)
256 {
257   if (clear_type & (THROUGHPUT_OVERALL|THROUGHPUT_CURRENT)) {
258     int i;
259
260     for (i = 0; i < t->SamplePeriod; i++)
261       t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
262     t->nSample = 0;
263   }
264
265   if (clear_type & THROUGHPUT_OVERALL) {
266     int divisor;
267
268     if ((divisor = throughput_uptime(t)) == 0)
269       divisor = 1;
270     prompt_Printf(prompt, "overall cleared (was %6qu bytes/sec)\n",
271                   (t->OctetsIn + t->OctetsOut) / divisor);
272     t->OctetsIn = t->OctetsOut = 0;
273     t->downtime = 0;
274     time(&t->uptime);
275   }
276
277   if (clear_type & THROUGHPUT_CURRENT) {
278     prompt_Printf(prompt, "current cleared (was %6qu bytes/sec in,"
279                   " %6qu bytes/sec out)\n",
280                   t->in.OctetsPerSecond, t->out.OctetsPerSecond);
281     t->in.OctetsPerSecond = t->out.OctetsPerSecond = 0;
282   }
283
284   if (clear_type & THROUGHPUT_PEAK) {
285     char *time_buf, *last;
286
287     time_buf = ctime(&t->BestOctetsPerSecondTime);
288     last = time_buf + strlen(time_buf);
289     if (last > time_buf && *--last == '\n')
290       *last = '\0';
291     prompt_Printf(prompt, "peak    cleared (was %6qu bytes/sec on %s)\n",
292                   t->BestOctetsPerSecond, time_buf);
293     t->BestOctetsPerSecond = 0;
294     time(&t->BestOctetsPerSecondTime);
295   }
296 }
297
298 void
299 throughput_callback(struct pppThroughput *t, void (*fn)(void *), void *data)
300 {
301   t->callback.fn = fn;
302   t->callback.data = data;
303 }