178189Sbrian/*-
2192798Sbrian * Copyright (c) 1996 - 2001, 2009 Brian Somers <brian@Awfulhak.org>
378189Sbrian *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
478189Sbrian *                           Internet Initiative Japan, Inc (IIJ)
578189Sbrian * All rights reserved.
66059Samurai *
778189Sbrian * Redistribution and use in source and binary forms, with or without
878189Sbrian * modification, are permitted provided that the following conditions
978189Sbrian * are met:
1078189Sbrian * 1. Redistributions of source code must retain the above copyright
1178189Sbrian *    notice, this list of conditions and the following disclaimer.
1278189Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1378189Sbrian *    notice, this list of conditions and the following disclaimer in the
1478189Sbrian *    documentation and/or other materials provided with the distribution.
156059Samurai *
1678189Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1778189Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1878189Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1978189Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2078189Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2178189Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2278189Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2378189Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2478189Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2578189Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2678189Sbrian * SUCH DAMAGE.
276059Samurai *
2850479Speter * $FreeBSD$
296059Samurai */
3030715Sbrian
3141799Sbrian#include <errno.h>
326059Samurai#include <signal.h>
33102500Sbrian#include <stdarg.h>
3436285Sbrian#include <stdio.h>
3547076Sbrian#include <string.h>
3696598Sbrian#include <sys/time.h>
3736285Sbrian#include <termios.h>
3830715Sbrian
3930715Sbrian#include "log.h"
4023840Sbrian#include "sig.h"
4130715Sbrian#include "timer.h"
4236285Sbrian#include "descriptor.h"
4336285Sbrian#include "prompt.h"
4423840Sbrian
4558030Sbrian
4658030Sbrian#define RESTVAL(t) \
4758030Sbrian    ((t).it_value.tv_sec * SECTICKS + (t).it_value.tv_usec / TICKUNIT + \
4858030Sbrian     ((((t).it_value.tv_usec % TICKUNIT) >= (TICKUNIT >> 1)) ? 1 : 0))
4958030Sbrian
5052267Sbrianstatic struct pppTimer *TimerList = NULL, *ExpiredList = NULL;
516059Samurai
5230715Sbrianstatic void StopTimerNoBlock(struct pppTimer *);
5330715Sbrian
5436285Sbrianstatic const char *
5536285SbriantState2Nam(u_int state)
5636285Sbrian{
5755146Sbrian  static const char * const StateNames[] = { "stopped", "running", "expired" };
5836285Sbrian
5936285Sbrian  if (state >= sizeof StateNames / sizeof StateNames[0])
6036285Sbrian    return "unknown";
6136285Sbrian  return StateNames[state];
6236285Sbrian}
6336285Sbrian
646059Samuraivoid
6546828Sbriantimer_Stop(struct pppTimer *tp)
666735Samurai{
6783404Sbrian  sigset_t mask, omask;
6828679Sbrian
6983404Sbrian  sigemptyset(&mask);
7083404Sbrian  sigaddset(&mask, SIGALRM);
7183404Sbrian  sigprocmask(SIG_BLOCK, &mask, &omask);
726735Samurai  StopTimerNoBlock(tp);
7383404Sbrian  sigprocmask(SIG_SETMASK, &omask, NULL);
746735Samurai}
7526516Sbrian
766735Samuraivoid
7746828Sbriantimer_Start(struct pppTimer *tp)
786059Samurai{
7953436Sbrian  struct itimerval itimer;
806059Samurai  struct pppTimer *t, *pt;
816059Samurai  u_long ticks = 0;
8283404Sbrian  sigset_t mask, omask;
8328679Sbrian
8483404Sbrian  sigemptyset(&mask);
8583404Sbrian  sigaddset(&mask, SIGALRM);
8683404Sbrian  sigprocmask(SIG_BLOCK, &mask, &omask);
876735Samurai
8836285Sbrian  if (tp->state != TIMER_STOPPED)
896735Samurai    StopTimerNoBlock(tp);
9036285Sbrian
916059Samurai  if (tp->load == 0) {
9236285Sbrian    log_Printf(LogTIMER, "%s timer[%p] has 0 load!\n", tp->name, tp);
9383404Sbrian    sigprocmask(SIG_SETMASK, &omask, NULL);
946059Samurai    return;
956059Samurai  }
9653436Sbrian
97192798Sbrian  /*
98192798Sbrian   * We just need to insert tp in the correct relative place.  We don't
99192798Sbrian   * need to adjust TimerList->rest (yet).
100192798Sbrian   */
10153436Sbrian  if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
102192798Sbrian    ticks = RESTVAL(itimer) - TimerList->rest;
10353436Sbrian
1046059Samurai  pt = NULL;
1056059Samurai  for (t = TimerList; t; t = t->next) {
1066059Samurai    if (ticks + t->rest >= tp->load)
1076059Samurai      break;
1086059Samurai    ticks += t->rest;
1096059Samurai    pt = t;
1106059Samurai  }
1116059Samurai
1126059Samurai  tp->state = TIMER_RUNNING;
1136059Samurai  tp->rest = tp->load - ticks;
11436285Sbrian
11536285Sbrian  if (t)
11636285Sbrian    log_Printf(LogTIMER, "timer_Start: Inserting %s timer[%p] before %s "
11736285Sbrian              "timer[%p], delta = %ld\n", tp->name, tp, t->name, t, tp->rest);
11836285Sbrian  else
11936285Sbrian    log_Printf(LogTIMER, "timer_Start: Inserting %s timer[%p]\n", tp->name, tp);
12036285Sbrian
1216059Samurai  /* Insert given *tp just before *t */
1226059Samurai  tp->next = t;
1236059Samurai  if (pt) {
1246059Samurai    pt->next = tp;
1257001Samurai  } else {
1266059Samurai    TimerList = tp;
12753436Sbrian    timer_InitService(t != NULL);	/* [re]Start the Timer Service */
1287001Samurai  }
1296059Samurai  if (t)
1306059Samurai    t->rest -= tp->rest;
1316735Samurai
13283404Sbrian  sigprocmask(SIG_SETMASK, &omask, NULL);
1336059Samurai}
1346059Samurai
13530715Sbrianstatic void
13646828SbrianStopTimerNoBlock(struct pppTimer *tp)
1376059Samurai{
138192798Sbrian  struct itimerval itimer;
1396059Samurai  struct pppTimer *t, *pt;
1406059Samurai
1416735Samurai  /*
14236285Sbrian   * A RUNNING timer must be removed from TimerList (->next list).
14336285Sbrian   * A STOPPED timer isn't in any list, but may have a bogus [e]next field.
14436285Sbrian   * An EXPIRED timer is in the ->enext list.
1456735Samurai   */
14652267Sbrian
14752267Sbrian  if (tp->state == TIMER_STOPPED)
1486059Samurai    return;
14952267Sbrian
1506059Samurai  pt = NULL;
15128679Sbrian  for (t = TimerList; t != tp && t != NULL; t = t->next)
1526059Samurai    pt = t;
15352267Sbrian
1546059Samurai  if (t) {
15558029Sbrian    if (pt)
1566059Samurai      pt->next = t->next;
15758029Sbrian    else {
1586059Samurai      TimerList = t->next;
15928679Sbrian      if (TimerList == NULL)	/* Last one ? */
16036285Sbrian	timer_TermService();	/* Terminate Timer Service */
1617001Samurai    }
16258029Sbrian    if (t->next) {
163192798Sbrian      if (!pt && getitimer(ITIMER_REAL, &itimer) == 0)
164192798Sbrian        t->next->rest += RESTVAL(itimer); /* t (tp) was the first in the list */
165192798Sbrian      else
166192798Sbrian        t->next->rest += t->rest;
167192798Sbrian      if (!pt && t->next->rest > 0)   /* t->next is now the first in the list */
16858029Sbrian        timer_InitService(1);
16958029Sbrian    }
17052267Sbrian  } else {
17152267Sbrian    /* Search for any pending expired timers */
17252267Sbrian    pt = NULL;
17352267Sbrian    for (t = ExpiredList; t != tp && t != NULL; t = t->enext)
17452267Sbrian      pt = t;
17526516Sbrian
17652267Sbrian    if (t) {
17752267Sbrian      if (pt)
17852267Sbrian        pt->enext = t->enext;
17952267Sbrian      else
18052267Sbrian        ExpiredList = t->enext;
18152267Sbrian    } else if (tp->state == TIMER_RUNNING)
18252267Sbrian      log_Printf(LogERROR, "Oops, %s timer not found!!\n", tp->name);
18352267Sbrian  }
18452267Sbrian
18552267Sbrian  tp->next = tp->enext = NULL;
1866059Samurai  tp->state = TIMER_STOPPED;
1876059Samurai}
1886059Samurai
18932663Sbrianstatic void
19036285SbrianTimerService(void)
1916059Samurai{
19241799Sbrian  struct pppTimer *tp, *exp, *next;
1936059Samurai
19436285Sbrian  if (log_IsKept(LogTIMER)) {
19537010Sbrian    static time_t t;		/* Only show timers globally every second */
19637010Sbrian    time_t n = time(NULL);
19736285Sbrian
19836285Sbrian    if (n > t)
19936285Sbrian      timer_Show(LogTIMER, NULL);
20036285Sbrian    t = n;
20136285Sbrian  }
20241799Sbrian
20313379Sphk  tp = TimerList;
20413379Sphk  if (tp) {
20541799Sbrian    tp->rest = 0;
20628679Sbrian
20741799Sbrian    /* Multiple timers might expire at once. Create a list of expired timers */
20841799Sbrian    exp = NULL;
20941799Sbrian    do {
21041799Sbrian      tp->state = TIMER_EXPIRED;
21141799Sbrian      next = tp->next;
21241799Sbrian      tp->enext = exp;
21341799Sbrian      exp = tp;
21441799Sbrian      tp = next;
21541799Sbrian    } while (tp && tp->rest == 0);
21698243Sbrian
21741799Sbrian    TimerList = tp;
21841799Sbrian    if (TimerList != NULL)	/* Any timers remaining ? */
21941799Sbrian      timer_InitService(1);	/* Restart the Timer Service */
22041799Sbrian    else
22141799Sbrian      timer_TermService();	/* Stop the Timer Service */
22298243Sbrian
22341799Sbrian    /* Process all expired timers */
22441799Sbrian    while (exp) {
22552267Sbrian      ExpiredList = exp->enext;
22641799Sbrian      exp->enext = NULL;
22741799Sbrian      if (exp->func)
22841799Sbrian        (*exp->func)(exp->arg);
22952267Sbrian      exp = ExpiredList;
2306059Samurai    }
2316059Samurai  }
2326059Samurai}
2336059Samurai
2346059Samuraivoid
23536285Sbriantimer_Show(int LogLevel, struct prompt *prompt)
2366059Samurai{
23747076Sbrian  struct itimerval itimer;
2386059Samurai  struct pppTimer *pt;
239192798Sbrian  long rest;
2406059Samurai
241192798Sbrian  /*
242192798Sbrian   * Adjust the base time so that the deltas reflect what's really
243192798Sbrian   * happening.  Changing TimerList->rest might cause it to become zero
244192798Sbrian   * (if getitimer() returns a value close to zero), and the
245192798Sbrian   * timer_InitService() call will call setitimer() with zero it_value,
246192798Sbrian   * stopping the itimer... so be careful!
247192798Sbrian   */
24847076Sbrian  if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
249192798Sbrian    rest = RESTVAL(itimer) - TimerList->rest;
250192798Sbrian  else
251192798Sbrian    rest = 0;
25247076Sbrian
25336285Sbrian#define SECS(val)	((val) / SECTICKS)
25436285Sbrian#define HSECS(val)	(((val) % SECTICKS) * 100 / SECTICKS)
25536285Sbrian#define DISP								\
25647076Sbrian  "%s timer[%p]: freq = %ld.%02lds, next = %lu.%02lus, state = %s\n",	\
25736285Sbrian  pt->name, pt, SECS(pt->load), HSECS(pt->load), SECS(rest),		\
25836285Sbrian  HSECS(rest), tState2Nam(pt->state)
2596735Samurai
26036285Sbrian  if (!prompt)
26136285Sbrian    log_Printf(LogLevel, "---- Begin of Timer Service List---\n");
26232063Sbrian
26336285Sbrian  for (pt = TimerList; pt; pt = pt->next) {
26436285Sbrian    rest += pt->rest;
26536285Sbrian    if (prompt)
26636285Sbrian      prompt_Printf(prompt, DISP);
26736285Sbrian    else
26836285Sbrian      log_Printf(LogLevel, DISP);
26930677Sache  }
27030677Sache
27136285Sbrian  if (!prompt)
27236285Sbrian    log_Printf(LogLevel, "---- End of Timer Service List ---\n");
27332063Sbrian}
27432063Sbrian
27598243Sbrianvoid
27641799Sbriantimer_InitService(int restart)
27728679Sbrian{
2787001Samurai  struct itimerval itimer;
2797001Samurai
28041799Sbrian  if (TimerList) {
28141799Sbrian    if (!restart)
28241799Sbrian      sig_signal(SIGALRM, (void (*)(int))TimerService);
28341799Sbrian    itimer.it_interval.tv_sec = 0;
28441799Sbrian    itimer.it_interval.tv_usec = 0;
28541799Sbrian    itimer.it_value.tv_sec = TimerList->rest / SECTICKS;
28641799Sbrian    itimer.it_value.tv_usec = (TimerList->rest % SECTICKS) * TICKUNIT;
28741799Sbrian    if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
28847076Sbrian      log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
28941799Sbrian  }
2907001Samurai}
2917001Samurai
29298243Sbrianvoid
29336285Sbriantimer_TermService(void)
29428679Sbrian{
2957001Samurai  struct itimerval itimer;
2967001Samurai
29725616Sbrian  itimer.it_interval.tv_usec = itimer.it_interval.tv_sec = 0;
2987001Samurai  itimer.it_value.tv_usec = itimer.it_value.tv_sec = 0;
29926516Sbrian  if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
30047076Sbrian    log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
30136285Sbrian  sig_signal(SIGALRM, SIG_IGN);
3027001Samurai}
303