Annotation of embedaddon/strongswan/src/libcharon/plugins/vici/vici_dispatcher.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (C) 2014 Martin Willi
        !             3:  * Copyright (C) 2014 revosec AG
        !             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: /*
        !            17:  * Copyright (C) 2014 Timo Teräs <timo.teras@iki.fi>
        !            18:  *
        !            19:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !            20:  * of this software and associated documentation files (the "Software"), to deal
        !            21:  * in the Software without restriction, including without limitation the rights
        !            22:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            23:  * copies of the Software, and to permit persons to whom the Software is
        !            24:  * furnished to do so, subject to the following conditions:
        !            25:  *
        !            26:  * The above copyright notice and this permission notice shall be included in
        !            27:  * all copies or substantial portions of the Software.
        !            28:  *
        !            29:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            30:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            31:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        !            32:  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            33:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            34:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            35:  * THE SOFTWARE.
        !            36:  */
        !            37: 
        !            38: #include "vici_dispatcher.h"
        !            39: #include "vici_socket.h"
        !            40: 
        !            41: #include <bio/bio_reader.h>
        !            42: #include <bio/bio_writer.h>
        !            43: #include <threading/mutex.h>
        !            44: #include <threading/condvar.h>
        !            45: #include <threading/thread.h>
        !            46: #include <collections/array.h>
        !            47: #include <collections/hashtable.h>
        !            48: 
        !            49: typedef struct private_vici_dispatcher_t private_vici_dispatcher_t;
        !            50: 
        !            51: /**
        !            52:  * Private data of an vici_dispatcher_t object.
        !            53:  */
        !            54: struct private_vici_dispatcher_t {
        !            55: 
        !            56:        /**
        !            57:         * Public vici_dispatcher_t interface.
        !            58:         */
        !            59:        vici_dispatcher_t public;
        !            60: 
        !            61:        /**
        !            62:         * Socket to send/receive messages
        !            63:         */
        !            64:        vici_socket_t *socket;
        !            65: 
        !            66:        /**
        !            67:         * List of registered commands (char* => command_t*)
        !            68:         */
        !            69:        hashtable_t *cmds;
        !            70: 
        !            71:        /**
        !            72:         * List of known events, and registered clients (char* => event_t*)
        !            73:         */
        !            74:        hashtable_t *events;
        !            75: 
        !            76:        /**
        !            77:         * Mutex to lock hashtables
        !            78:         */
        !            79:        mutex_t *mutex;
        !            80: 
        !            81:        /**
        !            82:         * Condvar to signal command termination
        !            83:         */
        !            84:        condvar_t *cond;
        !            85: };
        !            86: 
        !            87: /**
        !            88:  * Registered command
        !            89:  */
        !            90: typedef struct {
        !            91:        /** command name */
        !            92:        char *name;
        !            93:        /** callback for command */
        !            94:        vici_command_cb_t cb;
        !            95:        /** user data to pass to callback */
        !            96:        void *user;
        !            97:        /** command currently in use? */
        !            98:        u_int uses;
        !            99: } command_t;
        !           100: 
        !           101: /**
        !           102:  * Registered event
        !           103:  */
        !           104: typedef struct {
        !           105:        /** event name */
        !           106:        char *name;
        !           107:        /** registered clients, as u_int */
        !           108:        array_t *clients;
        !           109:        /** event currently in use? */
        !           110:        u_int uses;
        !           111: } event_t;
        !           112: 
        !           113: /**
        !           114:  * Send a operation code, optionally with name and message
        !           115:  */
        !           116: static void send_op(private_vici_dispatcher_t *this, u_int id,
        !           117:                                        vici_operation_t op, char *name, vici_message_t *message)
        !           118: {
        !           119:        bio_writer_t *writer;
        !           120:        u_int len;
        !           121: 
        !           122:        len = sizeof(uint8_t);
        !           123:        if (name)
        !           124:        {
        !           125:                len += sizeof(uint8_t) + strlen(name);
        !           126:        }
        !           127:        if (message)
        !           128:        {
        !           129:                len += message->get_encoding(message).len;
        !           130:        }
        !           131:        writer = bio_writer_create(len);
        !           132:        writer->write_uint8(writer, op);
        !           133:        if (name)
        !           134:        {
        !           135:                writer->write_data8(writer, chunk_from_str(name));
        !           136:        }
        !           137:        if (message)
        !           138:        {
        !           139:                writer->write_data(writer, message->get_encoding(message));
        !           140:        }
        !           141:        this->socket->send(this->socket, id, writer->extract_buf(writer));
        !           142:        writer->destroy(writer);
        !           143: }
        !           144: 
        !           145: /**
        !           146:  * Register client for event
        !           147:  */
        !           148: static void register_event(private_vici_dispatcher_t *this, char *name,
        !           149:                                                   u_int id)
        !           150: {
        !           151:        event_t *event;
        !           152: 
        !           153:        this->mutex->lock(this->mutex);
        !           154:        while (TRUE)
        !           155:        {
        !           156:                event = this->events->get(this->events, name);
        !           157:                if (!event)
        !           158:                {
        !           159:                        break;
        !           160:                }
        !           161:                if (!event->uses)
        !           162:                {
        !           163:                        array_insert(event->clients, ARRAY_TAIL, &id);
        !           164:                        break;
        !           165:                }
        !           166:                this->cond->wait(this->cond, this->mutex);
        !           167:        }
        !           168:        this->mutex->unlock(this->mutex);
        !           169: 
        !           170:        if (event)
        !           171:        {
        !           172:                DBG2(DBG_CFG, "vici client %u registered for: %s", id, name);
        !           173:                send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
        !           174:        }
        !           175:        else
        !           176:        {
        !           177:                DBG1(DBG_CFG, "vici client %u invalid registration: %s", id, name);
        !           178:                send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
        !           179:        }
        !           180: }
        !           181: 
        !           182: /**
        !           183:  * Unregister client for event
        !           184:  */
        !           185: static void unregister_event(private_vici_dispatcher_t *this, char *name,
        !           186:                                                         u_int id)
        !           187: {
        !           188:        enumerator_t *enumerator;
        !           189:        event_t *event;
        !           190:        u_int *current;
        !           191:        bool found = FALSE;
        !           192: 
        !           193:        this->mutex->lock(this->mutex);
        !           194:        while (TRUE)
        !           195:        {
        !           196:                event = this->events->get(this->events, name);
        !           197:                if (!event)
        !           198:                {
        !           199:                        break;
        !           200:                }
        !           201:                if (!event->uses)
        !           202:                {
        !           203:                        enumerator = array_create_enumerator(event->clients);
        !           204:                        while (enumerator->enumerate(enumerator, &current))
        !           205:                        {
        !           206:                                if (*current == id)
        !           207:                                {
        !           208:                                        array_remove_at(event->clients, enumerator);
        !           209:                                        found = TRUE;
        !           210:                                        break;
        !           211:                                }
        !           212:                        }
        !           213:                        enumerator->destroy(enumerator);
        !           214:                        break;
        !           215:                }
        !           216:                this->cond->wait(this->cond, this->mutex);
        !           217:        }
        !           218:        this->mutex->unlock(this->mutex);
        !           219: 
        !           220:        DBG2(DBG_CFG, "vici client %u unregistered for: %s", id, name);
        !           221: 
        !           222:        if (found)
        !           223:        {
        !           224:                send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
        !           225:        }
        !           226:        else
        !           227:        {
        !           228:                send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
        !           229:        }
        !           230: }
        !           231: 
        !           232: /**
        !           233:  * Data to release on thread cancellation
        !           234:  */
        !           235: typedef struct {
        !           236:        private_vici_dispatcher_t *this;
        !           237:        command_t *cmd;
        !           238:        vici_message_t *request;
        !           239: } release_data_t;
        !           240: 
        !           241: /**
        !           242:  * Release command after execution/cancellation
        !           243:  */
        !           244: CALLBACK(release_command, void,
        !           245:        release_data_t *release)
        !           246: {
        !           247:        release->request->destroy(release->request);
        !           248: 
        !           249:        release->this->mutex->lock(release->this->mutex);
        !           250:        if (--release->cmd->uses == 0)
        !           251:        {
        !           252:                release->this->cond->broadcast(release->this->cond);
        !           253:        }
        !           254:        release->this->mutex->unlock(release->this->mutex);
        !           255: 
        !           256:        free(release);
        !           257: }
        !           258: 
        !           259: /**
        !           260:  * Process a request message
        !           261:  */
        !           262: void process_request(private_vici_dispatcher_t *this, char *name, u_int id,
        !           263:                                         chunk_t data)
        !           264: {
        !           265:        vici_message_t *response = NULL;
        !           266:        release_data_t *release;
        !           267:        command_t *cmd;
        !           268: 
        !           269:        this->mutex->lock(this->mutex);
        !           270:        cmd = this->cmds->get(this->cmds, name);
        !           271:        if (cmd)
        !           272:        {
        !           273:                cmd->uses++;
        !           274:        }
        !           275:        this->mutex->unlock(this->mutex);
        !           276: 
        !           277:        if (cmd)
        !           278:        {
        !           279:                INIT(release,
        !           280:                        .this = this,
        !           281:                        .cmd = cmd,
        !           282:                );
        !           283: 
        !           284:                DBG2(DBG_CFG, "vici client %u requests: %s", id, name);
        !           285: 
        !           286:                thread_cleanup_push(release_command, release);
        !           287: 
        !           288:                release->request = vici_message_create_from_data(data, FALSE);
        !           289:                response = release->cmd->cb(cmd->user, cmd->name, id, release->request);
        !           290: 
        !           291:                thread_cleanup_pop(TRUE);
        !           292: 
        !           293:                if (response)
        !           294:                {
        !           295:                        send_op(this, id, VICI_CMD_RESPONSE, NULL, response);
        !           296:                        response->destroy(response);
        !           297:                }
        !           298:        }
        !           299:        else
        !           300:        {
        !           301:                DBG1(DBG_CFG, "vici client %u invalid request: %s", id, name);
        !           302:                send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL);
        !           303:        }
        !           304: }
        !           305: 
        !           306: CALLBACK(inbound, void,
        !           307:        private_vici_dispatcher_t *this, u_int id, chunk_t data)
        !           308: {
        !           309:        bio_reader_t *reader;
        !           310:        chunk_t chunk;
        !           311:        uint8_t type;
        !           312:        char name[257];
        !           313: 
        !           314:        reader = bio_reader_create(data);
        !           315:        if (reader->read_uint8(reader, &type))
        !           316:        {
        !           317:                switch (type)
        !           318:                {
        !           319:                        case VICI_EVENT_REGISTER:
        !           320:                                if (reader->read_data8(reader, &chunk) &&
        !           321:                                        vici_stringify(chunk, name, sizeof(name)))
        !           322:                                {
        !           323:                                        register_event(this, name, id);
        !           324:                                }
        !           325:                                else
        !           326:                                {
        !           327:                                        DBG1(DBG_CFG, "invalid vici register message");
        !           328:                                }
        !           329:                                break;
        !           330:                        case VICI_EVENT_UNREGISTER:
        !           331:                                if (reader->read_data8(reader, &chunk) &&
        !           332:                                        vici_stringify(chunk, name, sizeof(name)))
        !           333:                                {
        !           334:                                        unregister_event(this, name, id);
        !           335:                                }
        !           336:                                else
        !           337:                                {
        !           338:                                        DBG1(DBG_CFG, "invalid vici unregister message");
        !           339:                                }
        !           340:                                break;
        !           341:                        case VICI_CMD_REQUEST:
        !           342:                                if (reader->read_data8(reader, &chunk) &&
        !           343:                                        vici_stringify(chunk, name, sizeof(name)))
        !           344:                                {
        !           345:                                        thread_cleanup_push((void*)reader->destroy, reader);
        !           346:                                        process_request(this, name, id, reader->peek(reader));
        !           347:                                        thread_cleanup_pop(FALSE);
        !           348:                                }
        !           349:                                else
        !           350:                                {
        !           351:                                        DBG1(DBG_CFG, "invalid vici request message");
        !           352:                                }
        !           353:                                break;
        !           354:                        case VICI_CMD_RESPONSE:
        !           355:                        case VICI_EVENT_CONFIRM:
        !           356:                        case VICI_EVENT_UNKNOWN:
        !           357:                        case VICI_EVENT:
        !           358:                        default:
        !           359:                                DBG1(DBG_CFG, "unsupported vici operation: %u", type);
        !           360:                                break;
        !           361:                }
        !           362:        }
        !           363:        else
        !           364:        {
        !           365:                DBG1(DBG_CFG, "invalid vici message");
        !           366:        }
        !           367:        reader->destroy(reader);
        !           368: }
        !           369: 
        !           370: CALLBACK(connect_, void,
        !           371:        private_vici_dispatcher_t *this, u_int id)
        !           372: {
        !           373:        DBG2(DBG_CFG, "vici client %u connected", id);
        !           374: }
        !           375: 
        !           376: CALLBACK(disconnect, void,
        !           377:        private_vici_dispatcher_t *this, u_int id)
        !           378: {
        !           379:        enumerator_t *events, *ids;
        !           380:        event_t *event;
        !           381:        u_int *current;
        !           382: 
        !           383:        /* deregister client from all events */
        !           384:        this->mutex->lock(this->mutex);
        !           385:        events = this->events->create_enumerator(this->events);
        !           386:        while (events->enumerate(events, NULL, &event))
        !           387:        {
        !           388:                while (event->uses)
        !           389:                {
        !           390:                        this->cond->wait(this->cond, this->mutex);
        !           391:                }
        !           392:                ids = array_create_enumerator(event->clients);
        !           393:                while (ids->enumerate(ids, &current))
        !           394:                {
        !           395:                        if (id == *current)
        !           396:                        {
        !           397:                                array_remove_at(event->clients, ids);
        !           398:                        }
        !           399:                }
        !           400:                ids->destroy(ids);
        !           401:        }
        !           402:        events->destroy(events);
        !           403:        this->mutex->unlock(this->mutex);
        !           404: 
        !           405:        DBG2(DBG_CFG, "vici client %u disconnected", id);
        !           406: }
        !           407: 
        !           408: METHOD(vici_dispatcher_t, manage_command, void,
        !           409:        private_vici_dispatcher_t *this, char *name,
        !           410:        vici_command_cb_t cb, void *user)
        !           411: {
        !           412:        command_t *cmd;
        !           413: 
        !           414:        this->mutex->lock(this->mutex);
        !           415:        if (cb)
        !           416:        {
        !           417:                INIT(cmd,
        !           418:                        .name = strdup(name),
        !           419:                        .cb = cb,
        !           420:                        .user = user,
        !           421:                );
        !           422:                cmd = this->cmds->put(this->cmds, cmd->name, cmd);
        !           423:        }
        !           424:        else
        !           425:        {
        !           426:                cmd = this->cmds->remove(this->cmds, name);
        !           427:        }
        !           428:        if (cmd)
        !           429:        {
        !           430:                while (cmd->uses)
        !           431:                {
        !           432:                        this->cond->wait(this->cond, this->mutex);
        !           433:                }
        !           434:                free(cmd->name);
        !           435:                free(cmd);
        !           436:        }
        !           437:        this->mutex->unlock(this->mutex);
        !           438: }
        !           439: 
        !           440: METHOD(vici_dispatcher_t, manage_event, void,
        !           441:        private_vici_dispatcher_t *this, char *name, bool reg)
        !           442: {
        !           443:        event_t *event;
        !           444: 
        !           445:        this->mutex->lock(this->mutex);
        !           446:        if (reg)
        !           447:        {
        !           448:                INIT(event,
        !           449:                        .name = strdup(name),
        !           450:                        .clients = array_create(sizeof(u_int), 0),
        !           451:                );
        !           452:                event = this->events->put(this->events, event->name, event);
        !           453:        }
        !           454:        else
        !           455:        {
        !           456:                event = this->events->remove(this->events, name);
        !           457:        }
        !           458:        if (event)
        !           459:        {
        !           460:                while (event->uses)
        !           461:                {
        !           462:                        this->cond->wait(this->cond, this->mutex);
        !           463:                }
        !           464:                array_destroy(event->clients);
        !           465:                free(event->name);
        !           466:                free(event);
        !           467:        }
        !           468:        this->mutex->unlock(this->mutex);
        !           469: }
        !           470: 
        !           471: METHOD(vici_dispatcher_t, has_event_listeners, bool,
        !           472:        private_vici_dispatcher_t *this, char *name)
        !           473: {
        !           474:        event_t *event;
        !           475:        bool retval = FALSE;
        !           476: 
        !           477:        this->mutex->lock(this->mutex);
        !           478:        event = this->events->get(this->events, name);
        !           479:        if (event)
        !           480:        {
        !           481:                /* the entry might be getting destroyed, but returning
        !           482:                 * false positive is not a problem as a later raise_event
        !           483:                 * will check things again. */
        !           484:                retval = array_count(event->clients);
        !           485:        }
        !           486:        this->mutex->unlock(this->mutex);
        !           487: 
        !           488:        return retval;
        !           489: }
        !           490: 
        !           491: METHOD(vici_dispatcher_t, raise_event, void,
        !           492:        private_vici_dispatcher_t *this, char *name, u_int id,
        !           493:        vici_message_t *message)
        !           494: {
        !           495:        enumerator_t *enumerator;
        !           496:        event_t *event;
        !           497:        u_int *current;
        !           498: 
        !           499:        this->mutex->lock(this->mutex);
        !           500:        event = this->events->get(this->events, name);
        !           501:        if (event)
        !           502:        {
        !           503:                event->uses++;
        !           504:                this->mutex->unlock(this->mutex);
        !           505: 
        !           506:                enumerator = array_create_enumerator(event->clients);
        !           507:                while (enumerator->enumerate(enumerator, &current))
        !           508:                {
        !           509:                        if (id == 0 || id == *current)
        !           510:                        {
        !           511:                                send_op(this, *current, VICI_EVENT, name, message);
        !           512:                        }
        !           513:                }
        !           514:                enumerator->destroy(enumerator);
        !           515: 
        !           516:                this->mutex->lock(this->mutex);
        !           517:                if (--event->uses == 0)
        !           518:                {
        !           519:                        this->cond->broadcast(this->cond);
        !           520:                }
        !           521:        }
        !           522:        this->mutex->unlock(this->mutex);
        !           523: 
        !           524:        message->destroy(message);
        !           525: }
        !           526: 
        !           527: METHOD(vici_dispatcher_t, destroy, void,
        !           528:        private_vici_dispatcher_t *this)
        !           529: {
        !           530:        DESTROY_IF(this->socket);
        !           531:        this->mutex->destroy(this->mutex);
        !           532:        this->cond->destroy(this->cond);
        !           533:        this->cmds->destroy(this->cmds);
        !           534:        this->events->destroy(this->events);
        !           535:        free(this);
        !           536: }
        !           537: 
        !           538: /**
        !           539:  * See header
        !           540:  */
        !           541: vici_dispatcher_t *vici_dispatcher_create(char *uri)
        !           542: {
        !           543:        private_vici_dispatcher_t *this;
        !           544: 
        !           545:        INIT(this,
        !           546:                .public = {
        !           547:                        .manage_command = _manage_command,
        !           548:                        .manage_event = _manage_event,
        !           549:                        .has_event_listeners = _has_event_listeners,
        !           550:                        .raise_event = _raise_event,
        !           551:                        .destroy = _destroy,
        !           552:                },
        !           553:                .cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
        !           554:                .events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
        !           555:                .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        !           556:                .cond = condvar_create(CONDVAR_TYPE_DEFAULT),
        !           557:        );
        !           558: 
        !           559:        this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this);
        !           560:        if (!this->socket)
        !           561:        {
        !           562:                destroy(this);
        !           563:                return NULL;
        !           564:        }
        !           565: 
        !           566:        return &this->public;
        !           567: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>