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, version 2
27**
28**  mrouted 3.9-beta3 - COPYRIGHT 1989 by The Board of Trustees of
29**  Leland Stanford Junior University.
30**  - Original license can be found in the "doc/mrouted-LINCESE" file.
31**
32*/
33
34
35#include "defs.h"
36
37/* the code below implements a callout queue */
38static int id = 0;
39static struct timeOutQueue  *queue = 0; /* pointer to the beginning of timeout queue */
40
41struct timeOutQueue {
42    struct timeOutQueue    *next;   // Next event in queue
43    int                     id;
44    cfunc_t                 func;   // function to call
45    void                    *data;  // Data for function
46    int                     time;   // Time offset for next event
47};
48
49// Method for dumping the Queue to the igmp_syslog.
50static void debugQueue(void);
51
52/**
53*   Initializes the callout queue
54*/
55void callout_init() {
56    queue = NULL;
57}
58
59/**
60*   Clears all scheduled timeouts...
61*/
62void free_all_callouts() {
63    struct timeOutQueue *p;
64
65    while (queue) {
66        p = queue;
67        queue = queue->next;
68        free(p);
69    }
70}
71
72
73/**
74 * elapsed_time seconds have passed; perform all the events that should
75 * happen.
76 */
77void age_callout_queue(int elapsed_time) {
78    struct timeOutQueue *ptr;
79    int i = 0;
80
81    for (ptr = queue; ptr; ptr = queue, i++) {
82        if (ptr->time > elapsed_time) {
83            ptr->time -= elapsed_time;
84            return;
85        } else {
86            elapsed_time -= ptr->time;
87            queue = queue->next;
88            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "About to call timeout %d (#%d)", ptr->id, i);
89
90            if (ptr->func)
91                ptr->func(ptr->data);
92            free(ptr);
93        }
94    }
95}
96
97/**
98 * Return in how many seconds age_callout_queue() would like to be called.
99 * Return -1 if there are no events pending.
100 */
101int timer_nextTimer() {
102    if (queue) {
103        if (queue->time < 0) {
104            igmp_syslog(LOG_WARNING, 0, "timer_nextTimer top of queue says %d",
105                queue->time);
106            return 0;
107        }
108        return queue->time;
109    }
110    return -1;
111}
112
113/**
114 *  Inserts a timer in queue.
115 *  @param delay - Number of seconds the timeout should happen in.
116 *  @param action - The function to call on timeout.
117 *  @param data - Pointer to the function data to supply...
118 */
119int timer_setTimer(int delay, cfunc_t action, void *data) {
120    struct     timeOutQueue  *ptr, *node, *prev;
121    int i = 0;
122
123    /* create a node */
124    node = (struct timeOutQueue *)malloc(sizeof(struct timeOutQueue));
125    if (node == 0) {
126        igmp_syslog(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
127        return -1;
128    }
129    node->func = action;
130    node->data = data;
131    node->time = delay;
132    node->next = 0;
133    node->id   = ++id;
134
135    prev = ptr = queue;
136
137    /* insert node in the queue */
138
139    /* if the queue is empty, insert the node and return */
140    if (!queue) {
141        queue = node;
142    }
143    else {
144        /* chase the pointer looking for the right place */
145        while (ptr) {
146            if (delay < ptr->time) {
147                // We found the correct node
148                node->next = ptr;
149                if (ptr == queue) {
150                    queue = node;
151                }
152                else {
153                    prev->next = node;
154                }
155                ptr->time -= node->time;
156                IF_DEBUG {
157                    igmp_syslog(LOG_DEBUG, 0, "Created timeout %d (#%d) - delay %d secs",
158                        node->id, i, node->time);
159
160//                    debugQueue();
161                }
162                return node->id;
163            } else {
164                // Continur to check nodes.
165                delay -= ptr->time; node->time = delay;
166                prev = ptr;
167                ptr = ptr->next;
168            }
169            i++;
170        }
171        prev->next = node;
172    }
173    IF_DEBUG {
174        igmp_syslog(LOG_DEBUG, 0, "Created timeout %d (#%d) - delay %d secs",
175            node->id, i, node->time);
176
177//        debugQueue();
178    }
179
180    return node->id;
181}
182
183/**
184*   returns the time until the timer is scheduled
185*/
186int timer_leftTimer(int timer_id) {
187    struct timeOutQueue *ptr;
188    int left = 0;
189
190    if (!timer_id)
191        return -1;
192
193    for (ptr = queue; ptr; ptr = ptr->next) {
194        left += ptr->time;
195        if (ptr->id == timer_id) {
196            return left;
197        }
198    }
199    return -1;
200}
201
202/**
203*   clears the associated timer.  Returns 1 if succeeded.
204*/
205int timer_clearTimer(int  timer_id) {
206    struct timeOutQueue  *ptr, *prev;
207    int i = 0;
208
209    if (!timer_id)
210        return 0;
211
212    prev = ptr = queue;
213
214    /*
215     * find the right node, delete it. the subsequent node's time
216     * gets bumped up
217     */
218
219    IF_DEBUG debugQueue();
220    while (ptr) {
221        if (ptr->id == timer_id) {
222            /* got the right node */
223
224            /* unlink it from the queue */
225            if (ptr == queue)
226                queue = queue->next;
227            else
228                prev->next = ptr->next;
229
230            /* increment next node if any */
231            if (ptr->next != 0)
232                (ptr->next)->time += ptr->time;
233
234            if (ptr->data)
235                free(ptr->data);
236            IF_DEBUG igmp_syslog(LOG_DEBUG, 0, "deleted timer %d (#%d)", ptr->id, i);
237            free(ptr);
238            IF_DEBUG debugQueue();
239            return 1;
240        }
241        prev = ptr;
242        ptr = ptr->next;
243        i++;
244    }
245    // If we get here, the timer was not deleted.
246    IF_DEBUG {
247        igmp_syslog(LOG_DEBUG, 0, "failed to delete timer %d (#%d)", timer_id, i);
248        debugQueue();
249    }
250    return 0;
251}
252
253/**
254 * debugging utility
255 */
256static void debugQueue() {
257    struct timeOutQueue  *ptr;
258
259    IF_DEBUG {
260        for (ptr = queue; ptr; ptr = ptr->next) {
261            igmp_syslog(LOG_DEBUG, 0, "(Id:%d, Time:%d) ", ptr->id, ptr->time);
262        }
263    }
264}
265
266