Annotation of embedaddon/strongswan/src/libfast/fast_dispatcher.c, revision 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>