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