Home | History | Annotate | Download | only in src
      1 /*-
      2  * Copyright (c) 1997 Brian Somers <brian (at) 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.18.40.1 2010/12/21 17:10:29 kensmith Exp $
     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   %6qu bytes/sec\n",
    119                   (t->OctetsIn + t->OctetsOut) / divisor);
    120     prompt_Printf(prompt, "  %s %6qu bytes/sec in, %6qu 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      %6qu 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 = 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 %6qu bytes/sec)\n",
    270                   (t->OctetsIn + t->OctetsOut) / divisor);
    271     t->OctetsIn = t->OctetsOut = 0;
    272     t->downtime = 0;
    273     time(&t->uptime);
    274   }
    275 
    276   if (clear_type & THROUGHPUT_CURRENT) {
    277     prompt_Printf(prompt, "current cleared (was %6qu bytes/sec in,"
    278                   " %6qu 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 %6qu 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