1 /*
2 ** igmpproxy - IGMP proxy based multicast router
3 ** Copyright (C) 2005 Johnny Egeland <johnny@rlo.org>
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 **
19 **----------------------------------------------------------------------------
20 **
21 ** This software is derived work from the following software. The original
22 ** source code has been modified from it's original state by the author
23 ** of igmpproxy.
24 **
25 ** smcroute 0.92 - Copyright (C) 2001 Carsten Schill <carsten@cschill.de>
26 ** - Licensed under the GNU General Public License, either version 2 or
27 ** any later version.
28 **
29 ** mrouted 3.9-beta3 - Copyright (C) 2002 by The Board of Trustees of
30 ** Leland Stanford Junior University.
31 ** - Licensed under the 3-clause BSD license, see Stanford.txt file.
32 **
33 */
34
35
36 #include "igmpproxy.h"
37
38 /* the code below implements a callout queue */
39 static int id = 0;
40 static struct timeOutQueue *queue = 0; /* pointer to the beginning of timeout queue */
41
42 struct timeOutQueue {
43 struct timeOutQueue *next; // Next event in queue
44 int id;
45 timer_f func; // function to call
46 void *data; // Data for function
47 int time; // Time offset for next event
48 };
49
50 // Method for dumping the Queue to the log.
51 static void debugQueue(void);
52
53 /**
54 * Initializes the callout queue
55 */
callout_init(void)56 void callout_init(void) {
57 queue = NULL;
58 }
59
60 /**
61 * Clears all scheduled timeouts...
62 */
free_all_callouts(void)63 void free_all_callouts(void) {
64 struct timeOutQueue *p;
65
66 while (queue) {
67 p = queue;
68 queue = queue->next;
69 free(p);
70 }
71 }
72
73
74 /**
75 * elapsed_time seconds have passed; perform all the events that should
76 * happen.
77 */
age_callout_queue(int elapsed_time)78 void age_callout_queue(int elapsed_time) {
79 struct timeOutQueue *ptr;
80 struct timeOutQueue *_queue = NULL;
81 struct timeOutQueue *last = NULL;
82 int i = 0;
83
84 for (ptr = queue; ptr; ptr = ptr->next) {
85 if (ptr->time > elapsed_time) {
86 ptr->time -= elapsed_time;
87 break;
88 } else {
89 elapsed_time -= ptr->time;
90 if (_queue == NULL)
91 _queue = ptr;
92 last = ptr;
93 }
94 }
95
96 queue = ptr;
97 if (last) {
98 last->next = NULL;
99 }
100
101 /* process existing events */
102 for (ptr = _queue; ptr; ptr = _queue, i++) {
103 _queue = _queue->next;
104 my_log(LOG_DEBUG, 0, "About to call timeout %d (#%d)", ptr->id, i);
105 if (ptr->func)
106 ptr->func(ptr->data);
107 free(ptr);
108 }
109 }
110
111 /**
112 * Return in how many seconds age_callout_queue() would like to be called.
113 * Return -1 if there are no events pending.
114 */
timer_nextTimer(void)115 int timer_nextTimer(void) {
116 if (queue) {
117 if (queue->time < 0) {
118 my_log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d",
119 queue->time);
120 return 0;
121 }
122 return queue->time;
123 }
124 return -1;
125 }
126
127 /**
128 * Inserts a timer in queue.
129 * @param delay - Number of seconds the timeout should happen in.
130 * @param action - The function to call on timeout.
131 * @param data - Pointer to the function data to supply...
132 */
timer_setTimer(int delay,timer_f action,void * data)133 int timer_setTimer(int delay, timer_f action, void *data) {
134 struct timeOutQueue *ptr, *node, *prev;
135 int i = 0;
136
137 /* create a node */
138 node = (struct timeOutQueue *)malloc(sizeof(struct timeOutQueue));
139 if (node == 0) {
140 my_log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
141 return -1;
142 }
143 node->func = action;
144 node->data = data;
145 node->time = delay;
146 node->next = 0;
147 node->id = ++id;
148
149 prev = ptr = queue;
150
151 /* insert node in the queue */
152
153 /* if the queue is empty, insert the node and return */
154 if (!queue) {
155 queue = node;
156 }
157 else {
158 /* chase the pointer looking for the right place */
159 while (ptr) {
160 if (delay < ptr->time) {
161 // We found the correct node
162 node->next = ptr;
163 if (ptr == queue) {
164 queue = node;
165 }
166 else {
167 prev->next = node;
168 }
169 ptr->time -= node->time;
170 my_log(LOG_DEBUG, 0,
171 "Created timeout %d (#%d) - delay %d secs",
172 node->id, i, node->time);
173 debugQueue();
174 return node->id;
175 } else {
176 // Continur to check nodes.
177 delay -= ptr->time; node->time = delay;
178 prev = ptr;
179 ptr = ptr->next;
180 }
181 i++;
182 }
183 prev->next = node;
184 }
185 my_log(LOG_DEBUG, 0, "Created timeout %d (#%d) - delay %d secs",
186 node->id, i, node->time);
187 debugQueue();
188
189 return node->id;
190 }
191
192 /**
193 * returns the time until the timer is scheduled
194 */
timer_leftTimer(int timer_id)195 int timer_leftTimer(int timer_id) {
196 struct timeOutQueue *ptr;
197 int left = 0;
198
199 if (!timer_id)
200 return -1;
201
202 for (ptr = queue; ptr; ptr = ptr->next) {
203 left += ptr->time;
204 if (ptr->id == timer_id) {
205 return left;
206 }
207 }
208 return -1;
209 }
210
211 /**
212 * clears the associated timer. Returns 1 if succeeded.
213 */
timer_clearTimer(int timer_id)214 int timer_clearTimer(int timer_id) {
215 struct timeOutQueue *ptr, *prev;
216 int i = 0;
217
218 if (!timer_id)
219 return 0;
220
221 prev = ptr = queue;
222
223 /*
224 * find the right node, delete it. the subsequent node's time
225 * gets bumped up
226 */
227
228 debugQueue();
229 while (ptr) {
230 if (ptr->id == timer_id) {
231 /* got the right node */
232
233 /* unlink it from the queue */
234 if (ptr == queue)
235 queue = queue->next;
236 else
237 prev->next = ptr->next;
238
239 /* increment next node if any */
240 if (ptr->next != 0)
241 (ptr->next)->time += ptr->time;
242
243 if (ptr->data)
244 free(ptr->data);
245 my_log(LOG_DEBUG, 0, "deleted timer %d (#%d)", ptr->id, i);
246 free(ptr);
247 debugQueue();
248 return 1;
249 }
250 prev = ptr;
251 ptr = ptr->next;
252 i++;
253 }
254 // If we get here, the timer was not deleted.
255 my_log(LOG_DEBUG, 0, "failed to delete timer %d (#%d)", timer_id, i);
256 debugQueue();
257 return 0;
258 }
259
260 /**
261 * debugging utility
262 */
debugQueue(void)263 static void debugQueue(void) {
264 struct timeOutQueue *ptr;
265
266 for (ptr = queue; ptr; ptr = ptr->next) {
267 my_log(LOG_DEBUG, 0, "(Id:%d, Time:%d) ", ptr->id, ptr->time);
268 }
269 }
270