Annotation of embedaddon/strongswan/src/libfast/fast_dispatcher.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2007 Martin Willi
3: * HSR Hochschule fuer Technik Rapperswil
4: *
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>.
9: *
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
13: * for more details.
14: */
15:
16: #include "fast_dispatcher.h"
17:
18: #include "fast_request.h"
19: #include "fast_session.h"
20:
21: #include <fcgiapp.h>
22: #include <signal.h>
23: #include <unistd.h>
24: #include <errno.h>
25:
26: #include <utils/debug.h>
27: #include <threading/thread.h>
28: #include <threading/condvar.h>
29: #include <threading/mutex.h>
30: #include <collections/linked_list.h>
31: #include <collections/hashtable.h>
32:
33: /** Interval to check for expired sessions, in seconds */
34: #define CLEANUP_INTERVAL 30
35:
36: typedef struct private_fast_dispatcher_t private_fast_dispatcher_t;
37:
38: /**
39: * private data of the task manager
40: */
41: struct private_fast_dispatcher_t {
42:
43: /**
44: * public functions
45: */
46: fast_dispatcher_t public;
47:
48: /**
49: * fcgi socket fd
50: */
51: int fd;
52:
53: /**
54: * thread list
55: */
56: thread_t **threads;
57:
58: /**
59: * number of threads in "threads"
60: */
61: int thread_count;
62:
63: /**
64: * session locking mutex
65: */
66: mutex_t *mutex;
67:
68: /**
69: * Hashtable with active sessions
70: */
71: hashtable_t *sessions;
72:
73: /**
74: * session timeout
75: */
76: time_t timeout;
77:
78: /**
79: * timestamp of last session cleanup round
80: */
81: time_t last_cleanup;
82:
83: /**
84: * running in debug mode?
85: */
86: bool debug;
87:
88: /**
89: * List of controllers controller_constructor_t
90: */
91: linked_list_t *controllers;
92:
93: /**
94: * List of filters filter_constructor_t
95: */
96: linked_list_t *filters;
97:
98: /**
99: * constructor function to create session context (in controller_entry_t)
100: */
101: fast_context_constructor_t context_constructor;
102:
103: /**
104: * user param to context constructor
105: */
106: void *param;
107: };
108:
109: typedef struct {
110: /** constructor function */
111: fast_controller_constructor_t constructor;
112: /** parameter to constructor */
113: void *param;
114: } controller_entry_t;
115:
116: typedef struct {
117: /** constructor function */
118: fast_filter_constructor_t constructor;
119: /** parameter to constructor */
120: void *param;
121: } filter_entry_t;
122:
123: typedef struct {
124: /** session instance */
125: fast_session_t *session;
126: /** condvar to wait for session */
127: condvar_t *cond;
128: /** client host address, to prevent session hijacking */
129: char *host;
130: /** TRUE if session is in use */
131: bool in_use;
132: /** last use of the session */
133: time_t used;
134: /** has the session been closed by the handler? */
135: bool closed;
136: } session_entry_t;
137:
138: /**
139: * create a session and instantiate controllers
140: */
141: static fast_session_t* load_session(private_fast_dispatcher_t *this)
142: {
143: enumerator_t *enumerator;
144: controller_entry_t *centry;
145: filter_entry_t *fentry;
146: fast_session_t *session;
147: fast_context_t *context = NULL;
148: fast_controller_t *controller;
149: fast_filter_t *filter;
150:
151: if (this->context_constructor)
152: {
153: context = this->context_constructor(this->param);
154: }
155: session = fast_session_create(context);
156: if (!session)
157: {
158: return NULL;
159: }
160:
161: enumerator = this->controllers->create_enumerator(this->controllers);
162: while (enumerator->enumerate(enumerator, ¢ry))
163: {
164: controller = centry->constructor(context, centry->param);
165: session->add_controller(session, controller);
166: }
167: enumerator->destroy(enumerator);
168:
169: enumerator = this->filters->create_enumerator(this->filters);
170: while (enumerator->enumerate(enumerator, &fentry))
171: {
172: filter = fentry->constructor(context, fentry->param);
173: session->add_filter(session, filter);
174: }
175: enumerator->destroy(enumerator);
176:
177: return session;
178: }
179:
180: /**
181: * create a new session entry
182: */
183: static session_entry_t *session_entry_create(private_fast_dispatcher_t *this,
184: char *host)
185: {
186: session_entry_t *entry;
187: fast_session_t *session;
188:
189: session = load_session(this);
190: if (!session)
191: {
192: return NULL;
193: }
194: INIT(entry,
195: .cond = condvar_create(CONDVAR_TYPE_DEFAULT),
196: .session = session,
197: .host = strdup(host),
198: .used = time_monotonic(NULL),
199: );
200: return entry;
201: }
202:
203: /**
204: * destroy a session
205: */
206: static void session_entry_destroy(session_entry_t *entry)
207: {
208: entry->session->destroy(entry->session);
209: entry->cond->destroy(entry->cond);
210: free(entry->host);
211: free(entry);
212: }
213:
214: METHOD(fast_dispatcher_t, add_controller, void,
215: private_fast_dispatcher_t *this, fast_controller_constructor_t constructor,
216: void *param)
217: {
218: controller_entry_t *entry;
219:
220: INIT(entry,
221: .constructor = constructor,
222: .param = param,
223: );
224: this->controllers->insert_last(this->controllers, entry);
225: }
226:
227: METHOD(fast_dispatcher_t, add_filter, void,
228: private_fast_dispatcher_t *this, fast_filter_constructor_t constructor,
229: void *param)
230: {
231: filter_entry_t *entry;
232:
233: INIT(entry,
234: .constructor = constructor,
235: .param = param,
236: );
237: this->filters->insert_last(this->filters, entry);
238: }
239:
240: /**
241: * Hashtable hash function
242: */
243: static u_int session_hash(char *sid)
244: {
245: return chunk_hash(chunk_create(sid, strlen(sid)));
246: }
247:
248: /**
249: * Hashtable equals function
250: */
251: static bool session_equals(char *sid1, char *sid2)
252: {
253: return streq(sid1, sid2);
254: }
255:
256: /**
257: * Cleanup unused sessions
258: */
259: static void cleanup_sessions(private_fast_dispatcher_t *this, time_t now)
260: {
261: if (this->last_cleanup < now - CLEANUP_INTERVAL)
262: {
263: char *sid;
264: session_entry_t *entry;
265: enumerator_t *enumerator;
266: linked_list_t *remove;
267:
268: this->last_cleanup = now;
269: remove = linked_list_create();
270: enumerator = this->sessions->create_enumerator(this->sessions);
271: while (enumerator->enumerate(enumerator, &sid, &entry))
272: {
273: /* check all sessions for timeout or close flag */
274: if (!entry->in_use &&
275: (entry->used < now - this->timeout || entry->closed))
276: {
277: remove->insert_last(remove, sid);
278: }
279: }
280: enumerator->destroy(enumerator);
281:
282: while (remove->remove_last(remove, (void**)&sid) == SUCCESS)
283: {
284: entry = this->sessions->remove(this->sessions, sid);
285: if (entry)
286: {
287: session_entry_destroy(entry);
288: }
289: }
290: remove->destroy(remove);
291: }
292: }
293:
294: /**
295: * Actual dispatching code
296: */
297: static void dispatch(private_fast_dispatcher_t *this)
298: {
299: thread_cancelability(FALSE);
300:
301: while (TRUE)
302: {
303: fast_request_t *request;
304: session_entry_t *found = NULL;
305: time_t now;
306: char *sid;
307:
308: thread_cancelability(TRUE);
309: request = fast_request_create(this->fd, this->debug);
310: thread_cancelability(FALSE);
311:
312: if (request == NULL)
313: {
314: break;
315: }
316: now = time_monotonic(NULL);
317: sid = request->get_cookie(request, "SID");
318:
319: this->mutex->lock(this->mutex);
320: if (sid)
321: {
322: found = this->sessions->get(this->sessions, sid);
323: }
324: if (found && !streq(found->host, request->get_host(request)))
325: {
326: found = NULL;
327: }
328: if (found)
329: {
330: /* wait until session is unused */
331: while (found->in_use)
332: {
333: found->cond->wait(found->cond, this->mutex);
334: }
335: }
336: else
337: { /* create a new session if not found */
338: found = session_entry_create(this, request->get_host(request));
339: if (!found)
340: {
341: request->destroy(request);
342: this->mutex->unlock(this->mutex);
343: continue;
344: }
345: sid = found->session->get_sid(found->session);
346: this->sessions->put(this->sessions, sid, found);
347: }
348: found->in_use = TRUE;
349: this->mutex->unlock(this->mutex);
350:
351: /* start processing */
352: found->session->process(found->session, request);
353: found->used = time_monotonic(NULL);
354:
355: /* release session */
356: this->mutex->lock(this->mutex);
357: found->in_use = FALSE;
358: found->closed = request->session_closed(request);
359: found->cond->signal(found->cond);
360: cleanup_sessions(this, now);
361: this->mutex->unlock(this->mutex);
362:
363: request->destroy(request);
364: }
365: }
366:
367: METHOD(fast_dispatcher_t, run, void,
368: private_fast_dispatcher_t *this, int threads)
369: {
370: this->thread_count = threads;
371: this->threads = malloc(sizeof(thread_t*) * threads);
372: while (threads)
373: {
374: this->threads[threads - 1] = thread_create((thread_main_t)dispatch,
375: this);
376: if (this->threads[threads - 1])
377: {
378: threads--;
379: }
380: }
381: }
382:
383: METHOD(fast_dispatcher_t, waitsignal, void,
384: private_fast_dispatcher_t *this)
385: {
386: sigset_t set;
387:
388: sigemptyset(&set);
389: sigaddset(&set, SIGINT);
390: sigaddset(&set, SIGTERM);
391: sigaddset(&set, SIGHUP);
392: sigprocmask(SIG_BLOCK, &set, NULL);
393: while (sigwaitinfo(&set, NULL) == -1 && errno == EINTR)
394: {
395: /* wait for signal */
396: }
397: }
398:
399: METHOD(fast_dispatcher_t, destroy, void,
400: private_fast_dispatcher_t *this)
401: {
402: char *sid;
403: session_entry_t *entry;
404: enumerator_t *enumerator;
405:
406: FCGX_ShutdownPending();
407: while (this->thread_count--)
408: {
409: thread_t *thread = this->threads[this->thread_count];
410: thread->cancel(thread);
411: thread->join(thread);
412: }
413: enumerator = this->sessions->create_enumerator(this->sessions);
414: while (enumerator->enumerate(enumerator, &sid, &entry))
415: {
416: session_entry_destroy(entry);
417: }
418: enumerator->destroy(enumerator);
419: this->sessions->destroy(this->sessions);
420: this->controllers->destroy_function(this->controllers, free);
421: this->filters->destroy_function(this->filters, free);
422: this->mutex->destroy(this->mutex);
423: free(this->threads);
424: free(this);
425: }
426:
427: /*
428: * see header file
429: */
430: fast_dispatcher_t *fast_dispatcher_create(char *socket, bool debug, int timeout,
431: fast_context_constructor_t constructor, void *param)
432: {
433: private_fast_dispatcher_t *this;
434:
435: INIT(this,
436: .public = {
437: .add_controller = _add_controller,
438: .add_filter = _add_filter,
439: .run = _run,
440: .waitsignal = _waitsignal,
441: .destroy = _destroy,
442: },
443: .sessions = hashtable_create((void*)session_hash,
444: (void*)session_equals, 4096),
445: .controllers = linked_list_create(),
446: .filters = linked_list_create(),
447: .context_constructor = constructor,
448: .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
449: .param = param,
450: .timeout = timeout,
451: .last_cleanup = time_monotonic(NULL),
452: .debug = debug,
453: );
454:
455: FCGX_Init();
456:
457: if (socket)
458: {
459: unlink(socket);
460: this->fd = FCGX_OpenSocket(socket, 10);
461: }
462: return &this->public;
463: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>