Home | History | Annotate | Download | only in src
      1 /*-
      2  * Copyright (c) 1996 - 2001, 2009 Brian Somers <brian (at) Awfulhak.org>
      3  *          based on work by Toshiharu OHNO <tony-o (at) iij.ad.jp>
      4  *                           Internet Initiative Japan, Inc (IIJ)
      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  * $FreeBSD: src/usr.sbin/ppp/timer.c,v 1.47.24.1.4.1 2010/12/21 17:10:29 kensmith Exp $
     29  */
     30 
     31 #include <errno.h>
     32 #include <signal.h>
     33 #include <stdarg.h>
     34 #include <stdio.h>
     35 #include <string.h>
     36 #include <sys/time.h>
     37 #include <termios.h>
     38 
     39 #include "log.h"
     40 #include "sig.h"
     41 #include "timer.h"
     42 #include "descriptor.h"
     43 #include "prompt.h"
     44 
     45 
     46 #define RESTVAL(t) \
     47     ((t).it_value.tv_sec * SECTICKS + (t).it_value.tv_usec / TICKUNIT + \
     48      ((((t).it_value.tv_usec % TICKUNIT) >= (TICKUNIT >> 1)) ? 1 : 0))
     49 
     50 static struct pppTimer *TimerList = NULL, *ExpiredList = NULL;
     51 
     52 static void StopTimerNoBlock(struct pppTimer *);
     53 
     54 static const char *
     55 tState2Nam(u_int state)
     56 {
     57   static const char * const StateNames[] = { "stopped", "running", "expired" };
     58 
     59   if (state >= sizeof StateNames / sizeof StateNames[0])
     60     return "unknown";
     61   return StateNames[state];
     62 }
     63 
     64 void
     65 timer_Stop(struct pppTimer *tp)
     66 {
     67   sigset_t mask, omask;
     68 
     69   sigemptyset(&mask);
     70   sigaddset(&mask, SIGALRM);
     71   sigprocmask(SIG_BLOCK, &mask, &omask);
     72   StopTimerNoBlock(tp);
     73   sigprocmask(SIG_SETMASK, &omask, NULL);
     74 }
     75 
     76 void
     77 timer_Start(struct pppTimer *tp)
     78 {
     79   struct itimerval itimer;
     80   struct pppTimer *t, *pt;
     81   u_long ticks = 0;
     82   sigset_t mask, omask;
     83 
     84   sigemptyset(&mask);
     85   sigaddset(&mask, SIGALRM);
     86   sigprocmask(SIG_BLOCK, &mask, &omask);
     87 
     88   if (tp->state != TIMER_STOPPED)
     89     StopTimerNoBlock(tp);
     90 
     91   if (tp->load == 0) {
     92     log_Printf(LogTIMER, "%s timer[%p] has 0 load!\n", tp->name, tp);
     93     sigprocmask(SIG_SETMASK, &omask, NULL);
     94     return;
     95   }
     96 
     97   /*
     98    * We just need to insert tp in the correct relative place.  We don't
     99    * need to adjust TimerList->rest (yet).
    100    */
    101   if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
    102     ticks = RESTVAL(itimer) - TimerList->rest;
    103 
    104   pt = NULL;
    105   for (t = TimerList; t; t = t->next) {
    106     if (ticks + t->rest >= tp->load)
    107       break;
    108     ticks += t->rest;
    109     pt = t;
    110   }
    111 
    112   tp->state = TIMER_RUNNING;
    113   tp->rest = tp->load - ticks;
    114 
    115   if (t)
    116     log_Printf(LogTIMER, "timer_Start: Inserting %s timer[%p] before %s "
    117               "timer[%p], delta = %ld\n", tp->name, tp, t->name, t, tp->rest);
    118   else
    119     log_Printf(LogTIMER, "timer_Start: Inserting %s timer[%p]\n", tp->name, tp);
    120 
    121   /* Insert given *tp just before *t */
    122   tp->next = t;
    123   if (pt) {
    124     pt->next = tp;
    125   } else {
    126     TimerList = tp;
    127     timer_InitService(t != NULL);	/* [re]Start the Timer Service */
    128   }
    129   if (t)
    130     t->rest -= tp->rest;
    131 
    132   sigprocmask(SIG_SETMASK, &omask, NULL);
    133 }
    134 
    135 static void
    136 StopTimerNoBlock(struct pppTimer *tp)
    137 {
    138   struct itimerval itimer;
    139   struct pppTimer *t, *pt;
    140 
    141   /*
    142    * A RUNNING timer must be removed from TimerList (->next list).
    143    * A STOPPED timer isn't in any list, but may have a bogus [e]next field.
    144    * An EXPIRED timer is in the ->enext list.
    145    */
    146 
    147   if (tp->state == TIMER_STOPPED)
    148     return;
    149 
    150   pt = NULL;
    151   for (t = TimerList; t != tp && t != NULL; t = t->next)
    152     pt = t;
    153 
    154   if (t) {
    155     if (pt)
    156       pt->next = t->next;
    157     else {
    158       TimerList = t->next;
    159       if (TimerList == NULL)	/* Last one ? */
    160 	timer_TermService();	/* Terminate Timer Service */
    161     }
    162     if (t->next) {
    163       if (!pt && getitimer(ITIMER_REAL, &itimer) == 0)
    164         t->next->rest += RESTVAL(itimer); /* t (tp) was the first in the list */
    165       else
    166         t->next->rest += t->rest;
    167       if (!pt && t->next->rest > 0)   /* t->next is now the first in the list */
    168         timer_InitService(1);
    169     }
    170   } else {
    171     /* Search for any pending expired timers */
    172     pt = NULL;
    173     for (t = ExpiredList; t != tp && t != NULL; t = t->enext)
    174       pt = t;
    175 
    176     if (t) {
    177       if (pt)
    178         pt->enext = t->enext;
    179       else
    180         ExpiredList = t->enext;
    181     } else if (tp->state == TIMER_RUNNING)
    182       log_Printf(LogERROR, "Oops, %s timer not found!!\n", tp->name);
    183   }
    184 
    185   tp->next = tp->enext = NULL;
    186   tp->state = TIMER_STOPPED;
    187 }
    188 
    189 static void
    190 TimerService(void)
    191 {
    192   struct pppTimer *tp, *exp, *next;
    193 
    194   if (log_IsKept(LogTIMER)) {
    195     static time_t t;		/* Only show timers globally every second */
    196     time_t n = time(NULL);
    197 
    198     if (n > t)
    199       timer_Show(LogTIMER, NULL);
    200     t = n;
    201   }
    202 
    203   tp = TimerList;
    204   if (tp) {
    205     tp->rest = 0;
    206 
    207     /* Multiple timers might expire at once. Create a list of expired timers */
    208     exp = NULL;
    209     do {
    210       tp->state = TIMER_EXPIRED;
    211       next = tp->next;
    212       tp->enext = exp;
    213       exp = tp;
    214       tp = next;
    215     } while (tp && tp->rest == 0);
    216 
    217     TimerList = tp;
    218     if (TimerList != NULL)	/* Any timers remaining ? */
    219       timer_InitService(1);	/* Restart the Timer Service */
    220     else
    221       timer_TermService();	/* Stop the Timer Service */
    222 
    223     /* Process all expired timers */
    224     while (exp) {
    225       ExpiredList = exp->enext;
    226       exp->enext = NULL;
    227       if (exp->func)
    228         (*exp->func)(exp->arg);
    229       exp = ExpiredList;
    230     }
    231   }
    232 }
    233 
    234 void
    235 timer_Show(int LogLevel, struct prompt *prompt)
    236 {
    237   struct itimerval itimer;
    238   struct pppTimer *pt;
    239   long rest;
    240 
    241   /*
    242    * Adjust the base time so that the deltas reflect what's really
    243    * happening.  Changing TimerList->rest might cause it to become zero
    244    * (if getitimer() returns a value close to zero), and the
    245    * timer_InitService() call will call setitimer() with zero it_value,
    246    * stopping the itimer... so be careful!
    247    */
    248   if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
    249     rest = RESTVAL(itimer) - TimerList->rest;
    250   else
    251     rest = 0;
    252 
    253 #define SECS(val)	((val) / SECTICKS)
    254 #define HSECS(val)	(((val) % SECTICKS) * 100 / SECTICKS)
    255 #define DISP								\
    256   "%s timer[%p]: freq = %ld.%02lds, next = %lu.%02lus, state = %s\n",	\
    257   pt->name, pt, SECS(pt->load), HSECS(pt->load), SECS(rest),		\
    258   HSECS(rest), tState2Nam(pt->state)
    259 
    260   if (!prompt)
    261     log_Printf(LogLevel, "---- Begin of Timer Service List---\n");
    262 
    263   for (pt = TimerList; pt; pt = pt->next) {
    264     rest += pt->rest;
    265     if (prompt)
    266       prompt_Printf(prompt, DISP);
    267     else
    268       log_Printf(LogLevel, DISP);
    269   }
    270 
    271   if (!prompt)
    272     log_Printf(LogLevel, "---- End of Timer Service List ---\n");
    273 }
    274 
    275 void
    276 timer_InitService(int restart)
    277 {
    278   struct itimerval itimer;
    279 
    280   if (TimerList) {
    281     if (!restart)
    282       sig_signal(SIGALRM, (void (*)(int))TimerService);
    283     itimer.it_interval.tv_sec = 0;
    284     itimer.it_interval.tv_usec = 0;
    285     itimer.it_value.tv_sec = TimerList->rest / SECTICKS;
    286     itimer.it_value.tv_usec = (TimerList->rest % SECTICKS) * TICKUNIT;
    287     if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
    288       log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
    289   }
    290 }
    291 
    292 void
    293 timer_TermService(void)
    294 {
    295   struct itimerval itimer;
    296 
    297   itimer.it_interval.tv_usec = itimer.it_interval.tv_sec = 0;
    298   itimer.it_value.tv_usec = itimer.it_value.tv_sec = 0;
    299   if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
    300     log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
    301   sig_signal(SIGALRM, SIG_IGN);
    302 }
    303