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