2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include "dispatcher.h"
27 #include <utils/mutex.h>
28 #include <utils/linked_list.h>
29 #include <utils/hashtable.h>
31 /** Intervall to check for expired sessions, in seconds */
32 #define CLEANUP_INTERVAL 30
34 typedef struct private_dispatcher_t private_dispatcher_t
;
37 * private data of the task manager
39 struct private_dispatcher_t
{
57 * number of threads in "threads"
62 * session locking mutex
67 * Hahstable with active sessions
69 hashtable_t
*sessions
;
77 * timestamp of last session cleanup round
82 * running in debug mode?
87 * List of controllers controller_constructor_t
89 linked_list_t
*controllers
;
92 * List of filters filter_constructor_t
94 linked_list_t
*filters
;
97 * constructor function to create session context (in controller_entry_t)
99 context_constructor_t context_constructor
;
102 * user param to context constructor
108 /** constructor function */
109 controller_constructor_t constructor
;
110 /** parameter to constructor */
112 } controller_entry_t
;
115 /** constructor function */
116 filter_constructor_t constructor
;
117 /** parameter to constructor */
122 /** session instance */
124 /** condvar to wait for session */
126 /** client host address, to prevent session hijacking */
128 /** TRUE if session is in use */
130 /** last use of the session */
132 /** has the session been closed by the handler? */
137 * create a session and instanciate controllers
139 static session_t
* load_session(private_dispatcher_t
*this)
141 enumerator_t
*enumerator
;
142 controller_entry_t
*centry
;
143 filter_entry_t
*fentry
;
145 context_t
*context
= NULL
;
146 controller_t
*controller
;
149 if (this->context_constructor
)
151 context
= this->context_constructor(this->param
);
153 session
= session_create(context
);
155 enumerator
= this->controllers
->create_enumerator(this->controllers
);
156 while (enumerator
->enumerate(enumerator
, ¢ry
))
158 controller
= centry
->constructor(context
, centry
->param
);
159 session
->add_controller(session
, controller
);
161 enumerator
->destroy(enumerator
);
163 enumerator
= this->filters
->create_enumerator(this->filters
);
164 while (enumerator
->enumerate(enumerator
, &fentry
))
166 filter
= fentry
->constructor(context
, fentry
->param
);
167 session
->add_filter(session
, filter
);
169 enumerator
->destroy(enumerator
);
175 * create a new session entry
177 static session_entry_t
*session_entry_create(private_dispatcher_t
*this,
180 session_entry_t
*entry
;
182 entry
= malloc_thing(session_entry_t
);
183 entry
->in_use
= FALSE
;
184 entry
->closed
= FALSE
;
185 entry
->cond
= condvar_create(CONDVAR_TYPE_DEFAULT
);
186 entry
->session
= load_session(this);
187 entry
->used
= time_monotonic(NULL
);
188 entry
->host
= strdup(host
);
196 static void session_entry_destroy(session_entry_t
*entry
)
198 entry
->session
->destroy(entry
->session
);
199 entry
->cond
->destroy(entry
->cond
);
205 * Implementation of dispatcher_t.add_controller.
207 static void add_controller(private_dispatcher_t
*this,
208 controller_constructor_t constructor
, void *param
)
210 controller_entry_t
*entry
= malloc_thing(controller_entry_t
);
212 entry
->constructor
= constructor
;
213 entry
->param
= param
;
214 this->controllers
->insert_last(this->controllers
, entry
);
218 * Implementation of dispatcher_t.add_filter.
220 static void add_filter(private_dispatcher_t
*this,
221 filter_constructor_t constructor
, void *param
)
223 filter_entry_t
*entry
= malloc_thing(filter_entry_t
);
225 entry
->constructor
= constructor
;
226 entry
->param
= param
;
227 this->filters
->insert_last(this->filters
, entry
);
231 * Hashtable hash function
233 static u_int
session_hash(char *sid
)
235 return chunk_hash(chunk_create(sid
, strlen(sid
)));
239 * Hashtable equals function
241 static bool session_equals(char *sid1
, char *sid2
)
243 return streq(sid1
, sid2
);
247 * Cleanup unused sessions
249 static void cleanup_sessions(private_dispatcher_t
*this, time_t now
)
251 if (this->last_cleanup
< now
- CLEANUP_INTERVAL
)
254 session_entry_t
*entry
;
255 enumerator_t
*enumerator
;
256 linked_list_t
*remove
;
258 this->last_cleanup
= now
;
259 remove
= linked_list_create();
260 enumerator
= this->sessions
->create_enumerator(this->sessions
);
261 while (enumerator
->enumerate(enumerator
, &sid
, &entry
))
263 /* check all sessions for timeout or close flag */
264 if (!entry
->in_use
&&
265 (entry
->used
< now
- this->timeout
|| entry
->closed
))
267 remove
->insert_last(remove
, sid
);
270 enumerator
->destroy(enumerator
);
272 while (remove
->remove_last(remove
, (void**)&sid
) == SUCCESS
)
274 entry
= this->sessions
->remove(this->sessions
, sid
);
277 session_entry_destroy(entry
);
280 remove
->destroy(remove
);
285 * Actual dispatching code
287 static void dispatch(private_dispatcher_t
*this)
289 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
294 session_entry_t
*found
= NULL
;
298 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, NULL
);
299 request
= request_create(this->fd
, this->debug
);
300 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
306 now
= time_monotonic(NULL
);
307 sid
= request
->get_cookie(request
, "SID");
309 this->mutex
->lock(this->mutex
);
312 found
= this->sessions
->get(this->sessions
, sid
);
314 if (found
&& !streq(found
->host
, request
->get_host(request
)))
320 /* wait until session is unused */
321 while (found
->in_use
)
323 found
->cond
->wait(found
->cond
, this->mutex
);
327 { /* create a new session if not found */
328 found
= session_entry_create(this, request
->get_host(request
));
329 sid
= found
->session
->get_sid(found
->session
);
330 this->sessions
->put(this->sessions
, sid
, found
);
332 found
->in_use
= TRUE
;
333 this->mutex
->unlock(this->mutex
);
335 /* start processing */
336 found
->session
->process(found
->session
, request
);
337 found
->used
= time_monotonic(NULL
);
339 /* release session */
340 this->mutex
->lock(this->mutex
);
341 found
->in_use
= FALSE
;
342 found
->closed
= request
->session_closed(request
);
343 found
->cond
->signal(found
->cond
);
344 cleanup_sessions(this, now
);
345 this->mutex
->unlock(this->mutex
);
347 request
->destroy(request
);
352 * Implementation of dispatcher_t.run.
354 static void run(private_dispatcher_t
*this, int threads
)
356 this->thread_count
= threads
;
357 this->threads
= malloc(sizeof(pthread_t
) * threads
);
360 if (pthread_create(&this->threads
[threads
- 1],
361 NULL
, (void*)dispatch
, this) == 0)
369 * Implementation of dispatcher_t.waitsignal.
371 static void waitsignal(private_dispatcher_t
*this)
377 sigaddset(&set
, SIGINT
);
378 sigaddset(&set
, SIGTERM
);
379 sigaddset(&set
, SIGHUP
);
380 sigprocmask(SIG_BLOCK
, &set
, NULL
);
385 * Implementation of dispatcher_t.destroy
387 static void destroy(private_dispatcher_t
*this)
390 session_entry_t
*entry
;
391 enumerator_t
*enumerator
;
393 FCGX_ShutdownPending();
394 while (this->thread_count
--)
396 pthread_cancel(this->threads
[this->thread_count
]);
397 pthread_join(this->threads
[this->thread_count
], NULL
);
399 enumerator
= this->sessions
->create_enumerator(this->sessions
);
400 while (enumerator
->enumerate(enumerator
, &sid
, &entry
))
402 session_entry_destroy(entry
);
404 enumerator
->destroy(enumerator
);
405 this->sessions
->destroy(this->sessions
);
406 this->controllers
->destroy_function(this->controllers
, free
);
407 this->filters
->destroy_function(this->filters
, free
);
408 this->mutex
->destroy(this->mutex
);
416 dispatcher_t
*dispatcher_create(char *socket
, bool debug
, int timeout
,
417 context_constructor_t constructor
, void *param
)
419 private_dispatcher_t
*this = malloc_thing(private_dispatcher_t
);
421 this->public.add_controller
= (void(*)(dispatcher_t
*, controller_constructor_t
, void*))add_controller
;
422 this->public.add_filter
= (void(*)(dispatcher_t
*,filter_constructor_t constructor
, void *param
))add_filter
;
423 this->public.run
= (void(*)(dispatcher_t
*, int threads
))run
;
424 this->public.waitsignal
= (void(*)(dispatcher_t
*))waitsignal
;
425 this->public.destroy
= (void(*)(dispatcher_t
*))destroy
;
427 this->sessions
= hashtable_create((void*)session_hash
,
428 (void*)session_equals
, 4096);
429 this->controllers
= linked_list_create();
430 this->filters
= linked_list_create();
431 this->context_constructor
= constructor
;
432 this->mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
435 this->timeout
= timeout
;
436 this->last_cleanup
= time_monotonic(NULL
);
438 this->threads
= NULL
;
445 this->fd
= FCGX_OpenSocket(socket
, 10);
447 return &this->public;