Mon Jun 18 2012 03:55:33

Asterisk developer's documentation


evt.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2007, Digium, Inc.
00005  *
00006  * Russell Bryant <russell@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \author Russell Bryant <russell@digium.com>
00022  *
00023  * \brief Usage of the SAForum AIS (Application Interface Specification)
00024  *
00025  * \arg http://www.openais.org/
00026  *
00027  * This file contains the code specific to the use of the EVT
00028  * (Event) Service.
00029  */
00030 
00031 #include "asterisk.h"
00032 
00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 359053 $");
00034 
00035 #include <stdlib.h>
00036 #include <stdio.h>
00037 #include <string.h>
00038 #include <unistd.h>
00039 #include <errno.h>
00040 
00041 #include "ais.h"
00042 
00043 #include "asterisk/module.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/logger.h"
00047 #include "asterisk/event.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/linkedlists.h"
00050 #include "asterisk/devicestate.h"
00051 
00052 #ifndef AST_MODULE
00053 /* XXX HACK */
00054 #define AST_MODULE "res_ais"
00055 #endif
00056 
00057 SaEvtHandleT evt_handle;
00058 static SaAisErrorT evt_init_res;
00059 
00060 void evt_channel_open_cb(SaInvocationT invocation, SaEvtChannelHandleT channel_handle,
00061    SaAisErrorT error);
00062 void evt_event_deliver_cb(SaEvtSubscriptionIdT subscription_id,
00063    const SaEvtEventHandleT event_handle, const SaSizeT event_datalen);
00064 
00065 static const SaEvtCallbacksT evt_callbacks = {
00066    .saEvtChannelOpenCallback  = evt_channel_open_cb,
00067    .saEvtEventDeliverCallback = evt_event_deliver_cb,
00068 };
00069 
00070 static const struct {
00071    const char *str;
00072    enum ast_event_type type;
00073 } supported_event_types[] = {
00074    { "mwi", AST_EVENT_MWI },
00075    { "device_state", AST_EVENT_DEVICE_STATE_CHANGE },
00076 };
00077 
00078 /*! Used to provide unique id's to egress subscriptions */
00079 static int unique_id;
00080 
00081 struct subscribe_event {
00082    AST_LIST_ENTRY(subscribe_event) entry;
00083    /*! This is a unique identifier to identify this subscription in the event
00084     *  channel through the different API calls, subscribe, unsubscribe, and
00085     *  the event deliver callback. */
00086    SaEvtSubscriptionIdT id;
00087    enum ast_event_type type;
00088 };
00089 
00090 struct publish_event {
00091    AST_LIST_ENTRY(publish_event) entry;
00092    /*! We subscribe to events internally so that we can publish them
00093     *  on this event channel. */
00094    struct ast_event_sub *sub;
00095    enum ast_event_type type;
00096 };
00097 
00098 struct event_channel {
00099    AST_RWLIST_ENTRY(event_channel) entry;
00100    AST_LIST_HEAD_NOLOCK(, subscribe_event) subscribe_events;
00101    AST_LIST_HEAD_NOLOCK(, publish_event) publish_events;
00102    SaEvtChannelHandleT handle;
00103    char name[1];
00104 };
00105 
00106 static AST_RWLIST_HEAD_STATIC(event_channels, event_channel);
00107 
00108 void evt_channel_open_cb(SaInvocationT invocation, SaEvtChannelHandleT channel_handle,
00109    SaAisErrorT error)
00110 {
00111 
00112 }
00113 
00114 static void queue_event(struct ast_event *ast_event)
00115 {
00116    ast_event_queue_and_cache(ast_event);
00117 }
00118 
00119 void evt_event_deliver_cb(SaEvtSubscriptionIdT sub_id,
00120    const SaEvtEventHandleT event_handle, const SaSizeT event_datalen)
00121 {
00122    /* It is important to note that this works because we *know* that this
00123     * function will only be called by a single thread, the dispatch_thread.
00124     * If this module gets changed such that this is no longer the case, this
00125     * should get changed to a thread-local buffer, instead. */
00126    static unsigned char buf[4096];
00127    struct ast_event *event_dup, *event = (void *) buf;
00128    SaAisErrorT ais_res;
00129    SaSizeT len = sizeof(buf);
00130 
00131    if (event_datalen > len) {
00132       ast_log(LOG_ERROR, "Event received with size %u, which is too big\n"
00133          "for the allocated size %u. Change the code to increase the size.\n",
00134          (unsigned int) event_datalen, (unsigned int) len);
00135       return;
00136    }
00137 
00138    if (event_datalen < ast_event_minimum_length()) {
00139       ast_debug(1, "Ignoring event that's too small. %u < %u\n",
00140          (unsigned int) event_datalen,
00141          (unsigned int) ast_event_minimum_length());
00142       return;
00143    }
00144 
00145    ais_res = saEvtEventDataGet(event_handle, event, &len);
00146    if (ais_res != SA_AIS_OK) {
00147       ast_log(LOG_ERROR, "Error retrieving event payload: %s\n",
00148          ais_err2str(ais_res));
00149       return;
00150    }
00151 
00152    if (!ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(event, AST_EVENT_IE_EID))) {
00153       /* Don't feed events back in that originated locally. */
00154       return;
00155    }
00156 
00157    if (!(event_dup = ast_malloc(len)))
00158       return;
00159 
00160    memcpy(event_dup, event, len);
00161 
00162    queue_event(event_dup);
00163 }
00164 
00165 static const char *type_to_filter_str(enum ast_event_type type)
00166 {
00167    const char *filter_str = NULL;
00168    int i;
00169 
00170    for (i = 0; i < ARRAY_LEN(supported_event_types); i++) {
00171       if (supported_event_types[i].type == type) {
00172          filter_str = supported_event_types[i].str;
00173          break;
00174       }
00175    }
00176 
00177    return filter_str;
00178 }
00179 
00180 static void ast_event_cb(const struct ast_event *ast_event, void *data)
00181 {
00182    SaEvtEventHandleT event_handle;
00183    SaAisErrorT ais_res;
00184    struct event_channel *event_channel = data;
00185    SaClmClusterNodeT local_node;
00186    SaEvtEventPatternArrayT pattern_array;
00187    SaEvtEventPatternT pattern;
00188    SaSizeT len;
00189    const char *filter_str;
00190    SaEvtEventIdT event_id;
00191 
00192    ast_log(LOG_DEBUG, "Got an event to forward\n");
00193 
00194    if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
00195       /* If the event didn't originate from this server, don't send it back out. */
00196       ast_log(LOG_DEBUG, "Returning here\n");
00197       return;
00198    }
00199 
00200    ais_res = saEvtEventAllocate(event_channel->handle, &event_handle);
00201    if (ais_res != SA_AIS_OK) {
00202       ast_log(LOG_ERROR, "Error allocating event: %s\n", ais_err2str(ais_res));
00203       ast_log(LOG_DEBUG, "Returning here\n");
00204       return;
00205    }
00206 
00207    ais_res = saClmClusterNodeGet(clm_handle, SA_CLM_LOCAL_NODE_ID,
00208       SA_TIME_ONE_SECOND, &local_node);
00209    if (ais_res != SA_AIS_OK) {
00210       ast_log(LOG_ERROR, "Error getting local node name: %s\n", ais_err2str(ais_res));
00211       goto return_event_free;
00212    }
00213 
00214    filter_str = type_to_filter_str(ast_event_get_type(ast_event));
00215    len = strlen(filter_str) + 1;
00216    pattern.pattern = (SaUint8T *) filter_str;
00217    pattern.patternSize = len;
00218    pattern.allocatedSize = len;
00219 
00220    pattern_array.allocatedNumber = 1;
00221    pattern_array.patternsNumber = 1;
00222    pattern_array.patterns = &pattern;
00223 
00224    /*!
00225     * /todo Make retention time configurable
00226     * /todo Make event priorities configurable
00227     */
00228    ais_res = saEvtEventAttributesSet(event_handle, &pattern_array,
00229       SA_EVT_LOWEST_PRIORITY, SA_TIME_ONE_MINUTE, &local_node.nodeName);
00230    if (ais_res != SA_AIS_OK) {
00231       ast_log(LOG_ERROR, "Error setting event attributes: %s\n", ais_err2str(ais_res));
00232       goto return_event_free;
00233    }
00234 
00235    for (;;) {
00236       ais_res = saEvtEventPublish(event_handle,
00237          ast_event, ast_event_get_size(ast_event), &event_id);
00238       if (ais_res != SA_AIS_ERR_TRY_AGAIN) {
00239          break;
00240       }
00241       sched_yield();
00242    }
00243 
00244    if (ais_res != SA_AIS_OK) {
00245       ast_log(LOG_ERROR, "Error publishing event: %s\n", ais_err2str(ais_res));
00246       goto return_event_free;
00247    }
00248 
00249 return_event_free:
00250    ais_res = saEvtEventFree(event_handle);
00251    if (ais_res != SA_AIS_OK) {
00252       ast_log(LOG_ERROR, "Error freeing allocated event: %s\n", ais_err2str(ais_res));
00253    }
00254    ast_log(LOG_DEBUG, "Returning here (event_free)\n");
00255 }
00256 
00257 static char *ais_evt_show_event_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00258 {
00259    struct event_channel *event_channel;
00260 
00261    switch (cmd) {
00262    case CLI_INIT:
00263       e->command = "ais evt show event channels";
00264       e->usage =
00265          "Usage: ais evt show event channels\n"
00266          "       List configured event channels for the (EVT) Eventing service.\n";
00267       return NULL;
00268 
00269    case CLI_GENERATE:
00270       return NULL;   /* no completion */
00271    }
00272 
00273    if (a->argc != e->args)
00274       return CLI_SHOWUSAGE;
00275 
00276    ast_cli(a->fd, "\n"
00277                "=============================================================\n"
00278                "=== Event Channels ==========================================\n"
00279                "=============================================================\n"
00280                "===\n");
00281 
00282    AST_RWLIST_RDLOCK(&event_channels);
00283    AST_RWLIST_TRAVERSE(&event_channels, event_channel, entry) {
00284       struct publish_event *publish_event;
00285       struct subscribe_event *subscribe_event;
00286 
00287       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
00288                      "=== Event Channel Name: %s\n", event_channel->name);
00289 
00290       AST_LIST_TRAVERSE(&event_channel->publish_events, publish_event, entry) {
00291          ast_cli(a->fd, "=== ==> Publishing Event Type: %s\n",
00292             type_to_filter_str(publish_event->type));
00293       }
00294 
00295       AST_LIST_TRAVERSE(&event_channel->subscribe_events, subscribe_event, entry) {
00296          ast_cli(a->fd, "=== ==> Subscribing to Event Type: %s\n",
00297             type_to_filter_str(subscribe_event->type));
00298       }
00299 
00300       ast_cli(a->fd, "=== ---------------------------------------------------------\n"
00301                      "===\n");
00302    }
00303    AST_RWLIST_UNLOCK(&event_channels);
00304 
00305    ast_cli(a->fd, "=============================================================\n"
00306                   "\n");
00307 
00308    return CLI_SUCCESS;
00309 }
00310 
00311 static struct ast_cli_entry ais_cli[] = {
00312    AST_CLI_DEFINE(ais_evt_show_event_channels, "Show configured event channels"),
00313 };
00314 
00315 void ast_ais_evt_membership_changed(void)
00316 {
00317    struct event_channel *ec;
00318 
00319    AST_RWLIST_RDLOCK(&event_channels);
00320    AST_RWLIST_TRAVERSE(&event_channels, ec, entry) {
00321       struct publish_event *pe;
00322 
00323       AST_LIST_TRAVERSE(&ec->publish_events, pe, entry) {
00324          ast_debug(1, "Dumping cache for event channel '%s'\n", ec->name);
00325          ast_event_dump_cache(pe->sub);
00326       }
00327    }
00328    AST_RWLIST_UNLOCK(&event_channels);
00329 }
00330 
00331 static void add_publish_event(struct event_channel *event_channel, const char *event_type)
00332 {
00333    int i;
00334    enum ast_event_type type = -1;
00335    struct publish_event *publish_event;
00336 
00337    for (i = 0; i < ARRAY_LEN(supported_event_types); i++) {
00338       if (!strcasecmp(event_type, supported_event_types[i].str)) {
00339          type = supported_event_types[i].type;
00340          break;
00341       }
00342    }
00343 
00344    if (type == -1) {
00345       ast_log(LOG_WARNING, "publish_event option given with invalid value '%s'\n", event_type);
00346       return;
00347    }
00348 
00349    if (type == AST_EVENT_DEVICE_STATE_CHANGE && ast_enable_distributed_devstate()) {
00350       return;
00351    }
00352 
00353    if (!(publish_event = ast_calloc(1, sizeof(*publish_event)))) {
00354       return;
00355    }
00356 
00357    publish_event->type = type;
00358    ast_log(LOG_DEBUG, "Subscribing to event type %d\n", type);
00359    publish_event->sub = ast_event_subscribe(type, ast_event_cb, "AIS", event_channel,
00360       AST_EVENT_IE_END);
00361    ast_event_dump_cache(publish_event->sub);
00362 
00363    AST_LIST_INSERT_TAIL(&event_channel->publish_events, publish_event, entry);
00364 }
00365 
00366 static SaAisErrorT set_egress_subscription(struct event_channel *event_channel,
00367    struct subscribe_event *subscribe_event)
00368 {
00369    SaAisErrorT ais_res;
00370    SaEvtEventFilterArrayT filter_array;
00371    SaEvtEventFilterT filter;
00372    const char *filter_str = NULL;
00373    SaSizeT len;
00374 
00375    /* We know it's going to be valid.  It was checked earlier. */
00376    filter_str = type_to_filter_str(subscribe_event->type);
00377 
00378    filter.filterType = SA_EVT_EXACT_FILTER;
00379    len = strlen(filter_str) + 1;
00380    filter.filter.allocatedSize = len;
00381    filter.filter.patternSize = len;
00382    filter.filter.pattern = (SaUint8T *) filter_str;
00383 
00384    filter_array.filtersNumber = 1;
00385    filter_array.filters = &filter;
00386 
00387    ais_res = saEvtEventSubscribe(event_channel->handle, &filter_array,
00388       subscribe_event->id);
00389 
00390    return ais_res;
00391 }
00392 
00393 static void add_subscribe_event(struct event_channel *event_channel, const char *event_type)
00394 {
00395    int i;
00396    enum ast_event_type type = -1;
00397    struct subscribe_event *subscribe_event;
00398    SaAisErrorT ais_res;
00399 
00400    for (i = 0; i < ARRAY_LEN(supported_event_types); i++) {
00401       if (!strcasecmp(event_type, supported_event_types[i].str)) {
00402          type = supported_event_types[i].type;
00403          break;
00404       }
00405    }
00406 
00407    if (type == -1) {
00408       ast_log(LOG_WARNING, "subscribe_event option given with invalid value '%s'\n", event_type);
00409       return;
00410    }
00411 
00412    if (type == AST_EVENT_DEVICE_STATE_CHANGE && ast_enable_distributed_devstate()) {
00413       return;
00414    }
00415 
00416    if (!(subscribe_event = ast_calloc(1, sizeof(*subscribe_event)))) {
00417       return;
00418    }
00419 
00420    subscribe_event->type = type;
00421    subscribe_event->id = ast_atomic_fetchadd_int(&unique_id, +1);
00422 
00423    ais_res = set_egress_subscription(event_channel, subscribe_event);
00424    if (ais_res != SA_AIS_OK) {
00425       ast_log(LOG_ERROR, "Error setting up egress subscription: %s\n",
00426          ais_err2str(ais_res));
00427       free(subscribe_event);
00428       return;
00429    }
00430 
00431    AST_LIST_INSERT_TAIL(&event_channel->subscribe_events, subscribe_event, entry);
00432 }
00433 
00434 static void build_event_channel(struct ast_config *cfg, const char *cat)
00435 {
00436    struct ast_variable *var;
00437    struct event_channel *event_channel;
00438    SaAisErrorT ais_res;
00439    SaNameT sa_name = { 0, };
00440 
00441    AST_RWLIST_WRLOCK(&event_channels);
00442    AST_RWLIST_TRAVERSE(&event_channels, event_channel, entry) {
00443       if (!strcasecmp(event_channel->name, cat))
00444          break;
00445    }
00446    AST_RWLIST_UNLOCK(&event_channels);
00447    if (event_channel) {
00448       ast_log(LOG_WARNING, "Event channel '%s' was specified twice in "
00449          "configuration.  Second instance ignored.\n", cat);
00450       return;
00451    }
00452 
00453    if (!(event_channel = ast_calloc(1, sizeof(*event_channel) + strlen(cat))))
00454       return;
00455 
00456    strcpy(event_channel->name, cat);
00457    ast_copy_string((char *) sa_name.value, cat, sizeof(sa_name.value));
00458    sa_name.length = strlen((char *) sa_name.value);
00459    ais_res = saEvtChannelOpen(evt_handle, &sa_name,
00460       SA_EVT_CHANNEL_PUBLISHER | SA_EVT_CHANNEL_SUBSCRIBER | SA_EVT_CHANNEL_CREATE,
00461       SA_TIME_MAX, &event_channel->handle);
00462    if (ais_res != SA_AIS_OK) {
00463       ast_log(LOG_ERROR, "Error opening event channel: %s\n", ais_err2str(ais_res));
00464       free(event_channel);
00465       return;
00466    }
00467 
00468    for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
00469       if (!strcasecmp(var->name, "type")) {
00470          continue;
00471       } else if (!strcasecmp(var->name, "publish_event")) {
00472          add_publish_event(event_channel, var->value);
00473       } else if (!strcasecmp(var->name, "subscribe_event")) {
00474          add_subscribe_event(event_channel, var->value);
00475       } else {
00476          ast_log(LOG_WARNING, "Event channel '%s' contains invalid option '%s'\n",
00477             event_channel->name, var->name);
00478       }
00479    }
00480 
00481    AST_RWLIST_WRLOCK(&event_channels);
00482    AST_RWLIST_INSERT_TAIL(&event_channels, event_channel, entry);
00483    AST_RWLIST_UNLOCK(&event_channels);
00484 }
00485 
00486 static void load_config(void)
00487 {
00488    static const char filename[] = "ais.conf";
00489    struct ast_config *cfg;
00490    const char *cat = NULL;
00491    struct ast_flags config_flags = { 0 };
00492 
00493    if (!(cfg = ast_config_load(filename, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID)
00494       return;
00495 
00496    while ((cat = ast_category_browse(cfg, cat))) {
00497       const char *type;
00498 
00499       if (!strcasecmp(cat, "general"))
00500          continue;
00501 
00502       if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
00503          ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
00504             filename);
00505          continue;
00506       }
00507 
00508       if (!strcasecmp(type, "event_channel")) {
00509          build_event_channel(cfg, cat);
00510       } else {
00511          ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'\n",
00512             filename, type);
00513       }
00514    }
00515 
00516    ast_config_destroy(cfg);
00517 }
00518 
00519 static void publish_event_destroy(struct publish_event *publish_event)
00520 {
00521    ast_event_unsubscribe(publish_event->sub);
00522 
00523    free(publish_event);
00524 }
00525 
00526 static void subscribe_event_destroy(const struct event_channel *event_channel,
00527    struct subscribe_event *subscribe_event)
00528 {
00529    SaAisErrorT ais_res;
00530 
00531    /* saEvtChannelClose() will actually do this automatically, but it just
00532     * feels cleaner to go ahead and do it manually ... */
00533    ais_res = saEvtEventUnsubscribe(event_channel->handle, subscribe_event->id);
00534    if (ais_res != SA_AIS_OK) {
00535       ast_log(LOG_ERROR, "Error unsubscribing: %s\n", ais_err2str(ais_res));
00536    }
00537 
00538    free(subscribe_event);
00539 }
00540 
00541 static void event_channel_destroy(struct event_channel *event_channel)
00542 {
00543    struct publish_event *publish_event;
00544    struct subscribe_event *subscribe_event;
00545    SaAisErrorT ais_res;
00546 
00547    while ((publish_event = AST_LIST_REMOVE_HEAD(&event_channel->publish_events, entry)))
00548       publish_event_destroy(publish_event);
00549    while ((subscribe_event = AST_LIST_REMOVE_HEAD(&event_channel->subscribe_events, entry)))
00550       subscribe_event_destroy(event_channel, subscribe_event);
00551 
00552    ais_res = saEvtChannelClose(event_channel->handle);
00553    if (ais_res != SA_AIS_OK) {
00554       ast_log(LOG_ERROR, "Error closing event channel '%s': %s\n",
00555          event_channel->name, ais_err2str(ais_res));
00556    }
00557 
00558    free(event_channel);
00559 }
00560 
00561 static void destroy_event_channels(void)
00562 {
00563    struct event_channel *event_channel;
00564 
00565    AST_RWLIST_WRLOCK(&event_channels);
00566    while ((event_channel = AST_RWLIST_REMOVE_HEAD(&event_channels, entry))) {
00567       event_channel_destroy(event_channel);
00568    }
00569    AST_RWLIST_UNLOCK(&event_channels);
00570 }
00571 
00572 int ast_ais_evt_load_module(void)
00573 {
00574    evt_init_res = saEvtInitialize(&evt_handle, &evt_callbacks, &ais_version);
00575    if (evt_init_res != SA_AIS_OK) {
00576       ast_log(LOG_ERROR, "Could not initialize eventing service: %s\n",
00577          ais_err2str(evt_init_res));
00578       return -1;
00579    }
00580 
00581    load_config();
00582 
00583    ast_cli_register_multiple(ais_cli, ARRAY_LEN(ais_cli));
00584 
00585    return 0;
00586 }
00587 
00588 int ast_ais_evt_unload_module(void)
00589 {
00590    SaAisErrorT ais_res;
00591 
00592    if (evt_init_res != SA_AIS_OK) {
00593       return 0;
00594    }
00595 
00596    destroy_event_channels();
00597 
00598    ais_res = saEvtFinalize(evt_handle);
00599    if (ais_res != SA_AIS_OK) {
00600       ast_log(LOG_ERROR, "Problem stopping eventing service: %s\n",
00601          ais_err2str(ais_res));
00602       return -1;
00603    }
00604 
00605    return 0;
00606 }