1/*
2 * Layer Two Tunnelling Protocol Daemon
3 * Copyright (C) 1998 Adtran, Inc.
4 * Copyright (C) 2002 Jeff McAdams
5 *
6 * Mark Spencer
7 *
8 * This software is distributed under the terms
9 * of the GPL, which you should have received
10 * along with this source.
11 *
12 * Scheduler code for time based functionality
13 *
14 */
15
16#include <stdlib.h>
17#include <signal.h>
18#include <string.h>
19#include "l2tp.h"
20#include "scheduler.h"
21
22struct schedule_entry *events;
23static struct timeval zero;
24static sigset_t alarm;
25
26void init_scheduler (void)
27{
28    struct sigaction act;
29    act.sa_handler = alarm_handler;
30#if defined (LINUX) && (__i386__)
31    act.sa_restorer = NULL;
32#endif
33    act.sa_flags = 0;
34    sigemptyset (&act.sa_mask);
35    sigaddset (&act.sa_mask, SIGALRM);
36    sigaction (SIGALRM, &act, NULL);
37    events = NULL;
38    zero.tv_usec = 0;
39    zero.tv_sec = 0;
40    sigemptyset (&alarm);
41    sigaddset (&alarm, SIGALRM);
42}
43
44void alarm_handler (int signal)
45{
46    /* Check queue for events which should be
47       executed right now.  Execute them, then
48       see how long we should set the next timer
49     */
50    struct schedule_entry *p = events;
51    struct timeval now;
52    struct timeval then;
53    struct itimerval itv;
54    static int cnt = 0;
55    cnt++;
56    if (cnt != 1)
57    {
58        /* Whoa, we got called from within ourselves! */
59        log (LOG_DEBUG, "%s : Whoa... cnt = %d\n", __FUNCTION__, cnt);
60        return;
61    }
62    while (events)
63    {
64        gettimeofday (&now, NULL);
65        p = events;
66        if (TVLESSEQ (p->tv, now))
67        {
68            events = events->next;
69            /* This needs to be executed, as it has expired.
70               It is expected that p->func will free p->data
71               if it is necessary */
72            (*p->func) (p->data);
73            free (p);
74        }
75        else
76            break;
77    }
78    /* When we get here, either there are no more events
79       in the queue, or the remaining events need to happen
80       in the future, so we should schedule another alarm */
81    if (events)
82    {
83        then.tv_sec = events->tv.tv_sec - now.tv_sec;
84        then.tv_usec = events->tv.tv_usec - now.tv_usec;
85        if (then.tv_usec < 0)
86        {
87            then.tv_sec -= 1;
88            then.tv_usec += 1000000;
89        }
90        if ((then.tv_sec <= 0) && (then.tv_usec <= 0))
91        {
92            log (LOG_WARN, "%s: Whoa...  Scheduling for <=0 time???\n",
93                 __FUNCTION__);
94        }
95        else
96        {
97            itv.it_interval = zero;
98            itv.it_value = then;
99            setitimer (ITIMER_REAL, &itv, NULL);
100        }
101    }
102    cnt--;
103}
104
105void schedule_lock ()
106{
107    while (sigprocmask (SIG_BLOCK, &alarm, NULL));
108};
109
110void schedule_unlock ()
111{
112    /* See if we missed any events */
113/*	alarm_handler(0); */
114    while (sigprocmask (SIG_UNBLOCK, &alarm, NULL));
115    raise (SIGALRM);
116};
117
118struct schedule_entry *schedule (struct timeval tv, void (*func) (void *),
119                                 void *data)
120{
121    /* Schedule func to be run at relative time tv with data
122       as arguments.  If it has already expired, run it
123       immediately.  The queue should be in order of
124       increasing time */
125    struct schedule_entry *p = events, *q = NULL;
126    int need_timer = 0;
127    struct timeval diff;
128    struct itimerval itv;
129    diff = tv;
130    gettimeofday (&tv, NULL);
131    tv.tv_sec += diff.tv_sec;
132    tv.tv_usec += diff.tv_usec;
133    if (tv.tv_usec > 1000000)
134    {
135        tv.tv_sec++;
136        tv.tv_usec -= 1000000;
137    }
138    while (p)
139    {
140        if (TVLESS (tv, p->tv))
141            break;
142        q = p;
143        p = p->next;
144    };
145    if (q)
146    {
147        q->next =
148            (struct schedule_entry *) malloc (sizeof (struct schedule_entry));
149        q = q->next;
150    }
151    else
152    {
153        q = (struct schedule_entry *) malloc (sizeof (struct schedule_entry));
154        events = q;
155        need_timer = -1;
156    }
157    q->tv = tv;
158    q->func = func;
159    q->data = data;
160    q->next = p;
161    if (need_timer)
162    {
163        itv.it_interval = zero;
164        itv.it_value = diff;
165        setitimer (ITIMER_REAL, &itv, NULL);
166
167    }
168    return q;
169
170}
171
172inline struct schedule_entry *aschedule (struct timeval tv,
173                                         void (*func) (void *), void *data)
174{
175    /* Schedule func to be run at absolute time tv in the future with data
176       as arguments */
177    struct timeval now;
178    gettimeofday (&now, NULL);
179    tv.tv_usec -= now.tv_usec;
180    if (tv.tv_usec < 0)
181    {
182        tv.tv_usec += 1000000;
183        tv.tv_sec--;
184    }
185    tv.tv_sec -= now.tv_sec;
186    return schedule (tv, func, data);
187}
188
189void deschedule (struct schedule_entry *s)
190{
191    struct schedule_entry *p = events, *q = NULL;
192    if (!s)
193        return;
194    while (p)
195    {
196        if (p == s)
197        {
198            if (q)
199            {
200                q->next = p->next;
201            }
202            else
203            {
204                events = events->next;
205            }
206            free (p);
207            break;
208        }
209        q = p;
210        p = p->next;
211    }
212}
213