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