xref: /freebsd/usr.sbin/rtadvd/timer.c (revision 817420dc8eac7df799c78f5309b75092b7f7cd40)
1 /*	$KAME: timer.c,v 1.3 2000/05/22 22:23:07 itojun Exp $	*/
2 
3 /*
4  * Copyright (C) 1998 WIDE Project.
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  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 #include <sys/time.h>
35 
36 #include <unistd.h>
37 #include <syslog.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #if defined(__NetBSD__) || defined(__OpenBSD__)
41 #include <search.h>
42 #endif
43 #include "timer.h"
44 
45 static struct rtadvd_timer timer_head;
46 
47 #define MILLION 1000000
48 #define TIMEVAL_EQUAL(t1,t2) ((t1)->tv_sec == (t2)->tv_sec &&\
49  (t1)->tv_usec == (t2)->tv_usec)
50 
51 static struct timeval tm_max = {0x7fffffff, 0x7fffffff};
52 
53 void
54 rtadvd_timer_init()
55 {
56 	memset(&timer_head, 0, sizeof(timer_head));
57 
58 	timer_head.next = timer_head.prev = &timer_head;
59 	timer_head.tm = tm_max;
60 }
61 
62 struct rtadvd_timer *
63 rtadvd_add_timer(void (*timeout) __P((void *)),
64 		void (*update) __P((void *, struct timeval *)),
65 		 void *timeodata, void *updatedata)
66 {
67 	struct rtadvd_timer *newtimer;
68 
69 	if ((newtimer = malloc(sizeof(*newtimer))) == NULL) {
70 		syslog(LOG_ERR,
71 		       "<%s> can't allocate memory", __FUNCTION__);
72 		exit(1);
73 	}
74 
75 	memset(newtimer, 0, sizeof(*newtimer));
76 
77 	if (timeout == NULL) {
78 		syslog(LOG_ERR,
79 		       "<%s> timeout function unspecfied", __FUNCTION__);
80 		exit(1);
81 	}
82 	if (update == NULL) {
83 		syslog(LOG_ERR,
84 		       "<%s> update function unspecfied", __FUNCTION__);
85 		exit(1);
86 	}
87 	newtimer->expire = timeout;
88 	newtimer->update = update;
89 	newtimer->expire_data = timeodata;
90 	newtimer->update_data = updatedata;
91 	newtimer->tm = tm_max;
92 
93 	/* link into chain */
94 	insque(newtimer, &timer_head);
95 
96 	return(newtimer);
97 }
98 
99 void
100 rtadvd_remove_timer(struct rtadvd_timer **timer)
101 {
102 	remque(*timer);
103 	free(*timer);
104 	*timer = NULL;
105 }
106 
107 void
108 rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *timer)
109 {
110 	struct timeval now;
111 
112 	/* reset the timer */
113 	gettimeofday(&now, NULL);
114 
115 	TIMEVAL_ADD(&now, tm, &timer->tm);
116 
117 	/* update the next expiration time */
118 	if (TIMEVAL_LT(timer->tm, timer_head.tm))
119 		timer_head.tm = timer->tm;
120 
121 	return;
122 }
123 
124 /*
125  * Check expiration for each timer. If a timer is expired,
126  * call the expire function for the timer and update the timer.
127  * Return the next interval for select() call.
128  */
129 struct timeval *
130 rtadvd_check_timer()
131 {
132 	static struct timeval returnval;
133 	struct timeval now;
134 	struct rtadvd_timer *tm = timer_head.next;
135 
136 	gettimeofday(&now, NULL);
137 
138 	timer_head.tm = tm_max;
139 
140 	while(tm != &timer_head) {
141 		if (TIMEVAL_LEQ(tm->tm, now)) {
142 			(*tm->expire)(tm->expire_data);
143 			(*tm->update)(tm->update_data, &tm->tm);
144 			TIMEVAL_ADD(&tm->tm, &now, &tm->tm);
145 		}
146 
147 		if (TIMEVAL_LT(tm->tm, timer_head.tm))
148 			timer_head.tm = tm->tm;
149 
150 		tm = tm->next;
151 	}
152 
153 	if (TIMEVAL_EQUAL(&tm_max, &timer_head.tm)) {
154 		/* no need to timeout */
155 		return(NULL);
156 	}
157 	else if (TIMEVAL_LT(timer_head.tm, now)) {
158 		/* this may occur when the interval is too small */
159 		returnval.tv_sec = returnval.tv_usec = 0;
160 	}
161 	else
162 		TIMEVAL_SUB(&timer_head.tm, &now, &returnval);
163 	return(&returnval);
164 }
165 
166 struct timeval *
167 rtadvd_timer_rest(struct rtadvd_timer *timer)
168 {
169 	static struct timeval returnval, now;
170 
171 	gettimeofday(&now, NULL);
172 	if (TIMEVAL_LEQ(timer->tm, now)) {
173 		syslog(LOG_DEBUG,
174 		       "<%s> a timer must be expired, but not yet",
175 		       __FUNCTION__);
176 		returnval.tv_sec = returnval.tv_usec = 0;
177 	}
178 	else
179 		TIMEVAL_SUB(&timer->tm, &now, &returnval);
180 
181 	return(&returnval);
182 }
183 
184 /* result = a + b */
185 void
186 TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
187 {
188 	long l;
189 
190 	if ((l = a->tv_usec + b->tv_usec) < MILLION) {
191 		result->tv_usec = l;
192 		result->tv_sec = a->tv_sec + b->tv_sec;
193 	}
194 	else {
195 		result->tv_usec = l - MILLION;
196 		result->tv_sec = a->tv_sec + b->tv_sec + 1;
197 	}
198 }
199 
200 /*
201  * result = a - b
202  * XXX: this function assumes that a >= b.
203  */
204 void
205 TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
206 {
207 	long l;
208 
209 	if ((l = a->tv_usec - b->tv_usec) >= 0) {
210 		result->tv_usec = l;
211 		result->tv_sec = a->tv_sec - b->tv_sec;
212 	}
213 	else {
214 		result->tv_usec = MILLION + l;
215 		result->tv_sec = a->tv_sec - b->tv_sec - 1;
216 	}
217 }
218