df669ceb6f86c83bbefca7dd4407d4b5a8c2df37
[strongswan.git] / src / manager / lib / dispatcher.c
1 /**
2 * @file dispatcher.c
3 *
4 * @brief Implementation of dispatcher_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include "dispatcher.h"
24
25 #include "request.h"
26 #include "session.h"
27
28 #include <fcgiapp.h>
29 #include <pthread.h>
30 #include <signal.h>
31 #include <unistd.h>
32
33 #include <utils/linked_list.h>
34
35 typedef struct private_dispatcher_t private_dispatcher_t;
36
37 /**
38 * private data of the task manager
39 */
40 struct private_dispatcher_t {
41
42 /**
43 * public functions
44 */
45 dispatcher_t public;
46
47 /**
48 * fcgi socket fd
49 */
50 int fd;
51
52 /**
53 * thread list
54 */
55 pthread_t *threads;
56
57 /**
58 * number of threads in "threads"
59 */
60 int thread_count;
61
62 /**
63 * session locking mutex
64 */
65 pthread_mutex_t mutex;
66
67 /**
68 * List of sessions
69 */
70 linked_list_t *sessions;
71
72 /**
73 * session timeout
74 */
75 time_t timeout;
76
77 /**
78 * List of controllers controller_constructor_t
79 */
80 linked_list_t *controllers;
81
82 /**
83 * constructor function to create session context (in constructor_entry_t)
84 */
85 context_constructor_t context_constructor;
86
87 /**
88 * user param to context constructor
89 */
90 void *param;
91
92 /**
93 * thread specific initialization handler
94 */
95 void (*init)(void *param);
96
97 /**
98 * argument to pass to thread intiializer
99 */
100 void *init_param;
101
102 /**
103 * thread specific deinitialization handler
104 */
105 void (*deinit)(void *param);
106
107 /**
108 * param tho thread specific deinitialization handler
109 */
110 void *deinit_param;
111 };
112
113 typedef struct {
114 /** constructor function */
115 controller_constructor_t constructor;
116 /** parameter to constructor */
117 void *param;
118 } constructor_entry_t;
119
120 typedef struct {
121 /** session instance */
122 session_t *session;
123 /** condvar to wait for session */
124 pthread_cond_t cond;
125 /** number of threads waiting for session */
126 int waiting;
127 /** last use of the session */
128 time_t used;
129 } session_entry_t;
130
131 /**
132 * create a session and instanciate controllers
133 */
134 static session_t* load_session(private_dispatcher_t *this)
135 {
136 iterator_t *iterator;
137 constructor_entry_t *entry;
138 session_t *session;
139 context_t *context = NULL;
140 controller_t *controller;
141
142 if (this->context_constructor)
143 {
144 context = this->context_constructor(this->param);
145 }
146 session = session_create(context);
147
148 iterator = this->controllers->create_iterator(this->controllers, TRUE);
149 while (iterator->iterate(iterator, (void**)&entry))
150 {
151 controller = entry->constructor(context, entry->param);
152 session->add_controller(session, controller);
153 }
154 iterator->destroy(iterator);
155
156 return session;
157 }
158
159 /**
160 * create a new session entry
161 */
162 static session_entry_t *session_entry_create(private_dispatcher_t *this)
163 {
164 session_entry_t *entry;
165
166 entry = malloc_thing(session_entry_t);
167 entry->waiting = 1;
168 pthread_cond_init(&entry->cond, NULL);
169 entry->session = load_session(this);
170 entry->used = time(NULL);
171
172 return entry;
173 }
174
175 static void session_entry_destroy(session_entry_t *entry)
176 {
177 entry->session->destroy(entry->session);
178 free(entry);
179 }
180
181 /**
182 * Implementation of dispatcher_t.add_controller.
183 */
184 static void add_controller(private_dispatcher_t *this,
185 controller_constructor_t constructor, void *param)
186 {
187 constructor_entry_t *entry = malloc_thing(constructor_entry_t);
188
189 entry->constructor = constructor;
190 entry->param = param;
191 this->controllers->insert_last(this->controllers, entry);
192 }
193
194 /**
195 * Actual dispatching code
196 */
197 static void dispatch(private_dispatcher_t *this)
198 {
199 FCGX_Request fcgi_req;
200
201 if (FCGX_InitRequest(&fcgi_req, this->fd, 0) == 0)
202 {
203 while (TRUE)
204 {
205 request_t *request;
206 session_entry_t *current, *found = NULL;
207 iterator_t *iterator;
208 time_t now;
209 char *sid;
210 int accepted;
211
212 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
213 accepted = FCGX_Accept_r(&fcgi_req);
214 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
215
216 if (accepted != 0)
217 {
218 break;
219 }
220
221 /* prepare */
222 request = request_create(&fcgi_req, TRUE);
223 if (request == NULL)
224 {
225 continue;
226 }
227 sid = request->get_cookie(request, "SID");
228 now = time(NULL);
229
230 /* find session */
231 iterator = this->sessions->create_iterator_locked(this->sessions, &this->mutex);
232 while (iterator->iterate(iterator, (void**)&current))
233 {
234 /* check all sessions for timeout */
235 if (current->waiting == 0 &&
236 current->used < now - this->timeout)
237 {
238 iterator->remove(iterator);
239 session_entry_destroy(current);
240 continue;
241 }
242 if (!found && sid &&
243 streq(current->session->get_sid(current->session), sid))
244 {
245 found = current;
246 found->waiting++;
247 }
248 }
249 iterator->destroy(iterator);
250
251 if (found)
252 { /* wait until session is unused */
253 pthread_mutex_lock(&this->mutex);
254 while (found->waiting > 1)
255 {
256 pthread_cond_wait(&found->cond, &this->mutex);
257 }
258 pthread_mutex_unlock(&this->mutex);
259 }
260 else
261 { /* create a new session if not found */
262 found = session_entry_create(this);
263 pthread_mutex_lock(&this->mutex);
264 this->sessions->insert_first(this->sessions, found);
265 pthread_mutex_unlock(&this->mutex);
266 }
267
268 /* start processing */
269 found->session->process(found->session, request);
270 found->used = time(NULL);
271
272 /* release session */
273 pthread_mutex_lock(&this->mutex);
274 found->waiting--;
275 pthread_cond_signal(&found->cond);
276 pthread_mutex_unlock(&this->mutex);
277
278 /* cleanup */
279 request->destroy(request);
280
281 /*
282 FCGX_FPrintF(fcgi_req.out, "<ul>");
283 char **env = fcgi_req.envp;
284 while (*env)
285 {
286 FCGX_FPrintF(fcgi_req.out, "<li>%s</li>", *env);
287 env++;
288 }
289 FCGX_FPrintF(fcgi_req.out, "</ul>");
290 */
291 }
292 }
293 }
294
295 /**
296 * Setup thread and start dispatching
297 */
298 static void start_dispatching(private_dispatcher_t *this)
299 {
300 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
301 if (this->init)
302 {
303 this->init(this->init_param);
304 }
305 if (this->deinit)
306 {
307 pthread_cleanup_push(this->deinit, this->deinit_param);
308 dispatch(this);
309 pthread_cleanup_pop(1);
310 }
311 else
312 {
313 dispatch(this);
314 }
315 }
316
317 /**
318 * Implementation of dispatcher_t.run.
319 */
320 static void run(private_dispatcher_t *this, int threads,
321 void(*init)(void *param), void *init_param,
322 void(*deinit)(void *param), void *deinit_param)
323 {
324 this->init = init;
325 this->init_param = init_param;
326 this->deinit = deinit;
327 this->deinit_param = deinit_param;
328 this->thread_count = threads;
329 this->threads = malloc(sizeof(pthread_t) * threads);
330 while (threads)
331 {
332 if (pthread_create(&this->threads[threads - 1],
333 NULL, (void*)start_dispatching, this) == 0)
334 {
335 threads--;
336 }
337 }
338 }
339
340 /**
341 * Implementation of dispatcher_t.waitsignal.
342 */
343 static void waitsignal(private_dispatcher_t *this)
344 {
345 sigset_t set;
346 int sig;
347
348 sigemptyset(&set);
349 sigaddset(&set, SIGINT);
350 sigaddset(&set, SIGTERM);
351 sigaddset(&set, SIGHUP);
352 sigprocmask(SIG_BLOCK, &set, NULL);
353 sigwait(&set, &sig);
354 }
355
356 /**
357 * Implementation of dispatcher_t.destroy
358 */
359 static void destroy(private_dispatcher_t *this)
360 {
361 FCGX_ShutdownPending();
362 while (this->thread_count--)
363 {
364 pthread_cancel(this->threads[this->thread_count]);
365 pthread_join(this->threads[this->thread_count], NULL);
366 }
367 this->sessions->destroy_function(this->sessions, (void*)session_entry_destroy);
368 this->controllers->destroy_function(this->controllers, free);
369 free(this);
370 }
371
372 /*
373 * see header file
374 */
375 dispatcher_t *dispatcher_create(char *socket, int timeout,
376 context_constructor_t constructor, void *param)
377 {
378 private_dispatcher_t *this = malloc_thing(private_dispatcher_t);
379
380 this->public.add_controller = (void(*)(dispatcher_t*, controller_constructor_t, void*))add_controller;
381 this->public.run = (void(*)(dispatcher_t*, int threads,void(*)(void *),void *,void(*)(void *),void *))run;
382 this->public.waitsignal = (void(*)(dispatcher_t*))waitsignal;
383 this->public.destroy = (void(*)(dispatcher_t*))destroy;
384
385 this->sessions = linked_list_create();
386 this->controllers = linked_list_create();
387 this->context_constructor = constructor;
388 pthread_mutex_init(&this->mutex, NULL);
389 this->param = param;
390 this->fd = 0;
391 this->timeout = timeout;
392
393 FCGX_Init();
394
395 if (socket)
396 {
397 unlink(socket);
398 this->fd = FCGX_OpenSocket(socket, 10);
399 }
400 return &this->public;
401 }
402