xref: /freebsd/usr.sbin/ppp/timer.c (revision 33b77e2decd50e53798014b70bf7ca3bdc4c0c7e)
1 /*
2  *		PPP Timer Processing Module
3  *
4  *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5  *
6  *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the Internet Initiative Japan, Inc.  The name of the
14  * IIJ may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  * $Id: timer.c,v 1.25 1997/12/28 21:55:04 brian Exp $
21  *
22  *  TODO:
23  */
24 
25 #include <signal.h>
26 #ifdef SIGALRM
27 #include <errno.h>
28 #endif
29 #include <sys/time.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 
33 #include "command.h"
34 #include "mbuf.h"
35 #include "log.h"
36 #include "defs.h"
37 #include "sig.h"
38 #include "timer.h"
39 
40 struct pppTimer *TimerList = NULL;
41 
42 static void StopTimerNoBlock(struct pppTimer *);
43 static void InitTimerService(void);
44 
45 void
46 StopTimer(struct pppTimer * tp)
47 {
48 #ifdef SIGALRM
49   int omask;
50 
51   omask = sigblock(sigmask(SIGALRM));
52 #endif
53   StopTimerNoBlock(tp);
54 #ifdef SIGALRM
55   sigsetmask(omask);
56 #endif
57 }
58 
59 void
60 StartTimer(struct pppTimer * tp)
61 {
62   struct pppTimer *t, *pt;
63   u_long ticks = 0;
64 
65 #ifdef SIGALRM
66   int omask;
67 
68   omask = sigblock(sigmask(SIGALRM));
69 #endif
70 
71   if (tp->state != TIMER_STOPPED) {
72     StopTimerNoBlock(tp);
73   }
74   if (tp->load == 0) {
75     LogPrintf(LogDEBUG, "timer %x has 0 load!\n", tp);
76     sigsetmask(omask);
77     return;
78   }
79   pt = NULL;
80   for (t = TimerList; t; t = t->next) {
81     LogPrintf(LogDEBUG, "StartTimer: %x(%d):  ticks: %d, rest: %d\n",
82 	      t, t->state, ticks, t->rest);
83     if (ticks + t->rest >= tp->load)
84       break;
85     ticks += t->rest;
86     pt = t;
87   }
88 
89   tp->state = TIMER_RUNNING;
90   tp->rest = tp->load - ticks;
91   LogPrintf(LogDEBUG, "StartTimer: Inserting %x before %x, rest = %d\n",
92 	    tp, t, tp->rest);
93   /* Insert given *tp just before *t */
94   tp->next = t;
95   if (pt) {
96     pt->next = tp;
97   } else {
98     InitTimerService();
99     TimerList = tp;
100   }
101   if (t)
102     t->rest -= tp->rest;
103 
104 #ifdef SIGALRM
105   sigsetmask(omask);
106 #endif
107 }
108 
109 static void
110 StopTimerNoBlock(struct pppTimer * tp)
111 {
112   struct pppTimer *t, *pt;
113 
114   /*
115    * A Running Timer should be removing TimerList, But STOPPED/EXPIRED is
116    * already removing TimerList. So just marked as TIMER_STOPPED. Do not
117    * change tp->enext!! (Might be Called by expired proc)
118    */
119   LogPrintf(LogDEBUG, "StopTimer: %x, next = %x state=%x\n",
120 	    tp, tp->next, tp->state);
121   if (tp->state != TIMER_RUNNING) {
122     tp->next = NULL;
123     tp->state = TIMER_STOPPED;
124     return;
125   }
126   pt = NULL;
127   for (t = TimerList; t != tp && t != NULL; t = t->next)
128     pt = t;
129   if (t) {
130     if (pt) {
131       pt->next = t->next;
132     } else {
133       TimerList = t->next;
134       if (TimerList == NULL)	/* Last one ? */
135 	TermTimerService();	/* Terminate Timer Service */
136     }
137     if (t->next)
138       t->next->rest += tp->rest;
139   } else
140     LogPrintf(LogERROR, "Oops, timer not found!!\n");
141 
142   tp->next = NULL;
143   tp->state = TIMER_STOPPED;
144 }
145 
146 void
147 TimerService()
148 {
149   struct pppTimer *tp, *exp, *wt;
150 
151   if (LogIsKept(LogDEBUG))
152     ShowTimers();
153   tp = TimerList;
154   if (tp) {
155     tp->rest--;
156     if (tp->rest == 0) {
157 
158       /*
159        * Multiple timers may expires at once. Create list of expired timers.
160        */
161       exp = NULL;
162       do {
163 	tp->state = TIMER_EXPIRED;
164 	wt = tp->next;
165 	tp->enext = exp;
166 	exp = tp;
167 	LogPrintf(LogDEBUG, "TimerService: Add %x to exp\n", tp);
168 	tp = wt;
169       } while (tp && (tp->rest == 0));
170 
171       TimerList = tp;
172       if (TimerList == NULL)	/* No timers ? */
173 	TermTimerService();	/* Terminate Timer Service */
174       LogPrintf(LogDEBUG, "TimerService: next is %x(%d)\n",
175 		TimerList, TimerList ? TimerList->rest : 0);
176 
177       /*
178        * Process all expired timers.
179        */
180       while (exp) {
181 #ifdef notdef
182 	StopTimer(exp);
183 #endif
184 	if (exp->func)
185 	  (*exp->func) (exp->arg);
186 
187 	/*
188 	 * Just Removing each item from expired list And exp->enext will be
189 	 * intialized at next expire in this funtion.
190 	 */
191 	exp = exp->enext;
192       }
193     }
194   }
195 }
196 
197 void
198 ShowTimers()
199 {
200   struct pppTimer *pt;
201 
202   LogPrintf(LogDEBUG, "---- Begin of Timer Service List---\n");
203   for (pt = TimerList; pt; pt = pt->next)
204     LogPrintf(LogDEBUG, "%x: load = %d, rest = %d, state =%x\n",
205 	      pt, pt->load, pt->rest, pt->state);
206   LogPrintf(LogDEBUG, "---- End of Timer Service List ---\n");
207 }
208 
209 #ifdef SIGALRM
210 
211 static void
212 nointr_dosleep(u_int sec, u_int usec)
213 {
214   struct timeval to, st, et;
215 
216   gettimeofday(&st, NULL);
217   et.tv_sec = st.tv_sec + sec;
218   et.tv_usec = st.tv_usec + usec;
219   to.tv_sec = sec;
220   to.tv_usec = usec;
221   for (;;) {
222     if (select(0, NULL, NULL, NULL, &to) == 0 ||
223 	errno != EINTR) {
224       break;
225     } else {
226       gettimeofday(&to, NULL);
227       if (to.tv_sec > et.tv_sec + 1 ||
228           (to.tv_sec == et.tv_sec + 1 && to.tv_usec > et.tv_usec) ||
229           to.tv_sec < st.tv_sec ||
230           (to.tv_sec == st.tv_sec && to.tv_usec < st.tv_usec)) {
231         LogPrintf(LogWARN, "Clock adjusted between %d and %d seconds "
232                   "during sleep !\n",
233                   to.tv_sec - st.tv_sec, sec + to.tv_sec - st.tv_sec);
234         st.tv_sec = to.tv_sec;
235         st.tv_usec = to.tv_usec;
236         et.tv_sec = st.tv_sec + sec;
237         et.tv_usec = st.tv_usec + usec;
238         to.tv_sec = sec;
239         to.tv_usec = usec;
240       } else if (to.tv_sec > et.tv_sec ||
241                  (to.tv_sec == et.tv_sec && to.tv_usec >= et.tv_usec)) {
242         break;
243       } else {
244         to.tv_sec = et.tv_sec - to.tv_sec;
245         if (et.tv_usec < to.tv_usec) {
246           to.tv_sec--;
247           to.tv_usec = 1000000 + et.tv_usec - to.tv_usec;
248         } else
249           to.tv_usec = et.tv_usec - to.tv_usec;
250       }
251     }
252   }
253 }
254 
255 void
256 nointr_sleep(u_int sec)
257 {
258   nointr_dosleep(sec, 0);
259 }
260 
261 void
262 nointr_usleep(u_int usec)
263 {
264   nointr_dosleep(0, usec);
265 }
266 
267 static void
268 InitTimerService()
269 {
270   struct itimerval itimer;
271 
272   pending_signal(SIGALRM, (void (*) (int)) TimerService);
273   itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
274   itimer.it_interval.tv_usec = itimer.it_value.tv_usec = TICKUNIT;
275   if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
276     LogPrintf(LogERROR, "Unable to set itimer.\n");
277 }
278 
279 void
280 TermTimerService(void)
281 {
282   struct itimerval itimer;
283 
284   itimer.it_interval.tv_usec = itimer.it_interval.tv_sec = 0;
285   itimer.it_value.tv_usec = itimer.it_value.tv_sec = 0;
286   if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
287     LogPrintf(LogERROR, "Unable to set itimer.\n");
288   pending_signal(SIGALRM, SIG_IGN);
289 }
290 
291 #endif
292