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>
30 typedef struct private_dispatcher_t private_dispatcher_t
;
33 * private data of the task manager
35 struct private_dispatcher_t
{
53 * number of threads in "threads"
58 * session locking mutex
65 linked_list_t
*sessions
;
73 * running in debug mode?
78 * List of controllers controller_constructor_t
80 linked_list_t
*controllers
;
83 * List of filters filter_constructor_t
85 linked_list_t
*filters
;
88 * constructor function to create session context (in controller_entry_t)
90 context_constructor_t context_constructor
;
93 * user param to context constructor
99 /** constructor function */
100 controller_constructor_t constructor
;
101 /** parameter to constructor */
103 } controller_entry_t
;
106 /** constructor function */
107 filter_constructor_t constructor
;
108 /** parameter to constructor */
113 /** session instance */
115 /** condvar to wait for session */
117 /** client host address, to prevent session hijacking */
119 /** TRUE if session is in use */
121 /** last use of the session */
123 /** has the session been closed by the handler? */
128 * create a session and instanciate controllers
130 static session_t
* load_session(private_dispatcher_t
*this)
132 enumerator_t
*enumerator
;
133 controller_entry_t
*centry
;
134 filter_entry_t
*fentry
;
136 context_t
*context
= NULL
;
137 controller_t
*controller
;
140 if (this->context_constructor
)
142 context
= this->context_constructor(this->param
);
144 session
= session_create(context
);
146 enumerator
= this->controllers
->create_enumerator(this->controllers
);
147 while (enumerator
->enumerate(enumerator
, ¢ry
))
149 controller
= centry
->constructor(context
, centry
->param
);
150 session
->add_controller(session
, controller
);
152 enumerator
->destroy(enumerator
);
154 enumerator
= this->filters
->create_enumerator(this->filters
);
155 while (enumerator
->enumerate(enumerator
, &fentry
))
157 filter
= fentry
->constructor(context
, fentry
->param
);
158 session
->add_filter(session
, filter
);
160 enumerator
->destroy(enumerator
);
166 * create a new session entry
168 static session_entry_t
*session_entry_create(private_dispatcher_t
*this,
171 session_entry_t
*entry
;
173 entry
= malloc_thing(session_entry_t
);
174 entry
->in_use
= FALSE
;
175 entry
->closed
= FALSE
;
176 entry
->cond
= condvar_create(CONDVAR_TYPE_DEFAULT
);
177 entry
->session
= load_session(this);
178 entry
->used
= time_monotonic(NULL
);
179 entry
->host
= strdup(host
);
187 static void session_entry_destroy(session_entry_t
*entry
)
189 entry
->session
->destroy(entry
->session
);
190 entry
->cond
->destroy(entry
->cond
);
196 * Implementation of dispatcher_t.add_controller.
198 static void add_controller(private_dispatcher_t
*this,
199 controller_constructor_t constructor
, void *param
)
201 controller_entry_t
*entry
= malloc_thing(controller_entry_t
);
203 entry
->constructor
= constructor
;
204 entry
->param
= param
;
205 this->controllers
->insert_last(this->controllers
, entry
);
209 * Implementation of dispatcher_t.add_filter.
211 static void add_filter(private_dispatcher_t
*this,
212 filter_constructor_t constructor
, void *param
)
214 filter_entry_t
*entry
= malloc_thing(filter_entry_t
);
216 entry
->constructor
= constructor
;
217 entry
->param
= param
;
218 this->filters
->insert_last(this->filters
, entry
);
222 * Actual dispatching code
224 static void dispatch(private_dispatcher_t
*this)
226 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
231 session_entry_t
*current
, *found
= NULL
;
232 enumerator_t
*enumerator
;
236 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, NULL
);
237 request
= request_create(this->fd
, this->debug
);
238 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
244 sid
= request
->get_cookie(request
, "SID");
245 now
= time_monotonic(NULL
);
248 this->mutex
->lock(this->mutex
);
249 enumerator
= this->sessions
->create_enumerator(this->sessions
);
250 while (enumerator
->enumerate(enumerator
, ¤t
))
252 /* check all sessions for timeout or close flag
253 * TODO: use a seperate cleanup thread */
254 if (!current
->in_use
&&
255 (current
->used
< now
- this->timeout
|| current
->closed
))
257 this->sessions
->remove_at(this->sessions
, enumerator
);
258 session_entry_destroy(current
);
261 /* find by session ID. Prevent session hijacking by host check */
263 streq(current
->session
->get_sid(current
->session
), sid
) &&
264 streq(current
->host
, request
->get_host(request
)))
269 enumerator
->destroy(enumerator
);
273 /* wait until session is unused */
274 while (found
->in_use
)
276 found
->cond
->wait(found
->cond
, this->mutex
);
280 { /* create a new session if not found */
281 found
= session_entry_create(this, request
->get_host(request
));
282 this->sessions
->insert_first(this->sessions
, found
);
284 found
->in_use
= TRUE
;
285 this->mutex
->unlock(this->mutex
);
287 /* start processing */
288 found
->session
->process(found
->session
, request
);
289 found
->used
= time_monotonic(NULL
);
291 /* release session */
292 this->mutex
->lock(this->mutex
);
293 found
->in_use
= FALSE
;
294 found
->closed
= request
->session_closed(request
);
295 this->mutex
->unlock(this->mutex
);
296 found
->cond
->signal(found
->cond
);
299 request
->destroy(request
);
304 * Implementation of dispatcher_t.run.
306 static void run(private_dispatcher_t
*this, int threads
)
308 this->thread_count
= threads
;
309 this->threads
= malloc(sizeof(pthread_t
) * threads
);
312 if (pthread_create(&this->threads
[threads
- 1],
313 NULL
, (void*)dispatch
, this) == 0)
321 * Implementation of dispatcher_t.waitsignal.
323 static void waitsignal(private_dispatcher_t
*this)
329 sigaddset(&set
, SIGINT
);
330 sigaddset(&set
, SIGTERM
);
331 sigaddset(&set
, SIGHUP
);
332 sigprocmask(SIG_BLOCK
, &set
, NULL
);
337 * Implementation of dispatcher_t.destroy
339 static void destroy(private_dispatcher_t
*this)
341 FCGX_ShutdownPending();
342 while (this->thread_count
--)
344 pthread_cancel(this->threads
[this->thread_count
]);
345 pthread_join(this->threads
[this->thread_count
], NULL
);
347 this->sessions
->destroy_function(this->sessions
, (void*)session_entry_destroy
);
348 this->controllers
->destroy_function(this->controllers
, free
);
349 this->filters
->destroy_function(this->filters
, free
);
350 this->mutex
->destroy(this->mutex
);
358 dispatcher_t
*dispatcher_create(char *socket
, bool debug
, int timeout
,
359 context_constructor_t constructor
, void *param
)
361 private_dispatcher_t
*this = malloc_thing(private_dispatcher_t
);
363 this->public.add_controller
= (void(*)(dispatcher_t
*, controller_constructor_t
, void*))add_controller
;
364 this->public.add_filter
= (void(*)(dispatcher_t
*,filter_constructor_t constructor
, void *param
))add_filter
;
365 this->public.run
= (void(*)(dispatcher_t
*, int threads
))run
;
366 this->public.waitsignal
= (void(*)(dispatcher_t
*))waitsignal
;
367 this->public.destroy
= (void(*)(dispatcher_t
*))destroy
;
369 this->sessions
= linked_list_create();
370 this->controllers
= linked_list_create();
371 this->filters
= linked_list_create();
372 this->context_constructor
= constructor
;
373 this->mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
376 this->timeout
= timeout
;
378 this->threads
= NULL
;
385 this->fd
= FCGX_OpenSocket(socket
, 10);
387 return &this->public;