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, &centry))
        !           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>